On Tue, Dec 13, 2011 at 11:25 PM, Alan Malloy <a...@malloys.org> wrote: > On Dec 13, 7:56 pm, Stephen Compall <stephen.comp...@gmail.com> wrote: >> On Tue, 2011-12-13 at 16:28 -0800, Alan Malloy wrote: >> > As you can see, only as many elements are realized as are needed to >> > satisfy the user's request. >> >> Yes, in the expression (conr (conr (conr '( 1 2 3) 4) 6) 7), all the >> lazy-seqs implied by the conr calls must be forced immediately to yield >> (1 . more). It is the same problem with repeatedly concatting to the >> end, and with left-fold in a top-down evaluation scheme like Haskell: >> you can run out of stack if you must travel deep to get to the first >> element. > > I see. You're making a subtler point than I realized, and you're right > there. Repeated applications of conr need to be resolved all at once, > but conr'ing one item onto an already-realized collection doesn't > cause anything new to be realized until the element itself is needed.
One way to mitigate it would be to use a datastructure with a seq and a vector: conr on a seq would produce one of these with the conr'd item the sole element of the vector, and conr on one of these datastructures would conj the item onto the vector. The datastructure would behave as a seq: first on the datastructure would return first on the seq part, and next would return a datastructure with the seq part nexted -- only, if the seq had only one element, instead with (seq the-vector-part) as the seq part and an empty vector part. An invariant would be maintained that the seq part is nil iff the whole thing is empty. Of course, such a datastructure is a queue, and it's not too much more work to turn it into a deque. Adding at both ends in O(1) and peeking/popping at the front is already in there. Peeking/popping at the rear is trickier, because a naive implementation is O(n) if the vector part happens to be empty. Any time the vector part would become empty you'd have to split the data in half between the seq part and the vector part, in a kind of reversal of how ArrayLists and vectors grow in amortized O(1). The invariant would now be that if there are at least two elements both parts are nonempty, if there's one element the vector part is empty, and if there's none the seq part is nil. (And nobody give me any guff about the vector operations actually being O(log_32 n); on present and near-future hardware I don't think log_32 n will get much above 6 or 7, so for all practical purposes it differs from O(1) by a constant factor of overhead. The work being done on finger trees might enable deques with "true" O(1) peeks at both ends, if the trees hold direct references to their end elements in their roots -- but then I'd expect O(log n) adds and pops at both ends, since the depth will need to be walked to do those.) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en