[Haskell-cafe] Re: Data.Ring -- Pre-announce
Heinrich Apfelmus
apfelmus at quantentunnel.de
Thu Dec 31 06:27:09 EST 2009
John Van Enk wrote:
> Hi List,
>
> I recently needed a ring structure (circular list with bi-directional
> access) and didn't see anything obvious on Hackage. I threw something
> together fairly quickly and would like some feedback before tossing it on
> Hackage.
>
> I'd really appreciate if some one would:
>
> 1. make sure the code looks goodish (127 lines with full docs)
> 2. make sure my tests look saneish
>
> If I hear nothing, I'll assume wild support and push to Hackage.
But that's a neat data structure, thanks for putting it into a library.
:) Here my comments:
Since the name Ring is already taken by an ubiquitous mathematical
structure, and thus already in hackage for example as Algebra.Ring in
the numeric-prelude , I suggest to call the data structure Necklace
instead.
While we're at it, I'd also perform the following name changes:
-- new focus = element to the left of the current focus
prev - > left
-- new focus = element to the right of the current focus
next -> right
left -> elementsLeft
right -> elementsRight
The problem with prev and next is that they are relative to a
default direction and everybody would have to remember whether that was
to the left or right. Since a necklace is inherently symmetric, I
suggest to avoid imposing such a default direction.
Furthermore, I think the documentation is lacking a few key points,
first and foremost the explanation of what exactly a Ring (Necklace)
is. While you can assume that people should know what a stack or queue
is, this cannot be said for this little known data structure.
Second, there is the "off by one" issue. Does insert put elements left
or right of the focus?
Third, I think the quickcheck tests are not very effective; instead of
always converting to and from lists and testing how the operations
behave, I suggest to focus on "intrinsic" laws, like for example
(prev . next) x == x
focus . insert a x == Just a
and so on. (You do need one or two tests involving toList and
fromList , but no more.)
Also, you should make an instance Arbitrary a => Arbitrary (Necklace a)
to replace the [Int] arguments that are just a proxy for an actual
necklace. (Not to mention that thanks to parametric polymorphism, it is
sufficient to test everything on the lists [1..n])
Last but not least, what about asymptotic complexity? While your
operations are usually O(1), sometimes they are O(n). You provide a
function balance to deal with the latter case, but the API is much
more usable if you guarantee O(1) no matter what; please dispense with
balance and friends.
You can implement O(1) operations by using two queues instead of two
lists as left and right part. Alternatively, you can adapt the rotation
scheme for purely functional queues to your data structure and
internally balance whenever one of the lists becomes for example 3x
larger than the other one. See also chapter 3.4.2 of
Chris Okasaki. Purely Functional Data Structures. (thesis)
http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf
(Not sure if 3 is a good size factor; this can be determined with the
amortized cost/step graph c(a) = let b = a/(1+a)-1/2 in (b+1)/b where
a is the size factor.)
Regards,
Heinrich Apfelmus
--
http://apfelmus.nfshost.com
More information about the Haskell-Cafe
mailing list