No, it will be O(n), where the "cool magic" is in the use of lazy  
sequences.

Here's a very simple way to write an O(n) lazy concat:

user> (defn my-concat [& seqs]
        (when-first [s seqs]
          (if (seq s)
              (lazy-seq (cons (first s) (apply my-concat (rest s) (rest  
seqs))))
              (recur (rest seqs)))))

user> (my-concat '(1 2 3) [4 5] '(6 7))
(1 2 3 4 5 6 7)

This is not proper lazy programming style, but it should give you the  
basic idea.

The actual definition of concat is significantly more complicated:

user> (source concat)
(defn concat
   "Returns a lazy seq representing the concatenation of the elements  
in the supplied colls."
   ([] (lazy-seq nil))
   ([x] (lazy-seq x))
   ([x y]
      (lazy-seq
       (let [s (seq x)]
         (if s
           (cons (first s) (concat (rest s) y))
           y))))
   ([x y & zs]
      (let [cat (fn cat [xys zs]
                  (lazy-seq
                   (let [xys (seq xys)]
                     (if xys
                       (cons (first xys) (cat (rest xys) zs))
                       (when zs
                         (cat (first zs) (next zs)))))))]
            (cat (concat x y) zs))))

-Jason



On Mar 1, 2009, at 9:47 PM, Zededarian wrote:

>
> Thanks!
>
> One follow up question, though: will the first solution take O(n^2)
> time, or is clojure clever enough to keep track of where the end of a
> sequence is on its own (or, alternately, to start by concatenating the
> last two elements and working backward)?  Or does it do some other
> cool magic to make this quick?
>
> On Mar 1, 9:07 pm, Jason Wolfe <jawo...@berkeley.edu> wrote:
>> Hi,
>>
>> The problem is that you end up with the front of your worklist being
>> wrapped in <n-lines> nested calls to lazy-seq, one per each call to
>> concat.  Then, realizing the first element causes a stack overflow.
>> This wouldn't happen if you reversed the arguments to concat, but  
>> then
>> you wouldn't get what you want.  Some possibilities (untested):
>>
>> ; probably the shortest, most idiomatic solution?
>>
>> (defn getstrlist [file]
>>   (apply concat
>>     (take-while identity
>>       (repeatedly #(getline file)))))
>>
>> ;or, using vectors, and written more like your solution:
>>
>> (defn getstrlist [file]
>>   (loop [worklst []]
>>    (if-let [x (getline file)]
>>       (recur (into worklst x))
>>      worklst)))
>>
>> Cheers,
>> Jason
>>
>> On Mar 1, 4:53 pm, Zededarian <zededar...@gmail.com> wrote:
>>
>>> My ultimate goal is to get a list of all the words in a file in
>>> order.  I have a function that will get a list of all the words on  
>>> one
>>> line of this file in order, and I want to efficiently concatenate
>>> these lists to end up with one large list.  I'm working with about
>>> 12000 lines right now, but in a perfect world I'd like to scale much
>>> higher.
>>
>>> I started off just using concat.  Basically my code looks like:
>>> (defn getstrlist
>>>   ([file]
>>>      (loop [x (getline file) worklst '()]
>>>        (if x
>>>          (recur (getline file) (concat worklst (splitstr x)))
>>>          worklst))))
>>
>>> Then in the REPL, if I type (def a (getstrlist FILE)), it works  
>>> fine.
>>> But if I try to output a or take (first a), I get a stack overflow
>>> error.  I don't know why this is.  I remember hearing somewhere that
>>> Clojure had lazy sequences, so my best guess is that it isn't  
>>> actually
>>> concatenating anything, but is storing pointers to the start of all
>>> the lists on a stack that overflows when I try to evaluate one of
>>> these pointers.
>>
>>> I know in Scheme I would write this using metalists that keep  
>>> track of
>>> the location of their last element:
>>> (define (concat metalst1 metalst2)
>>>   (set-cdr! (car metalst1) (cdr metalst2))
>>>   (set-car! metalst1 (car metalst2))
>>>   metalst1)
>>
>>> (define (make-metalst lst)
>>>   (cons (get-end lst) lst))
>>
>>> (define (get-end lst)
>>>   (if (null? lst)
>>>       '()
>>>       (if (null? (cdr lst))
>>>           lst
>>>           (get-end (cdr lst)))))
>>
>>> Which would be fairly efficient.  But since Clojure doesn't seem to
>>> have real lists, I'm not quite sure how I could port this over.  I
>>> suppose I could implement my own cons pairs like an idiot and do all
>>> of this manually, but I figure I'm just not understanding the  
>>> "Clojure
>>> solution" to this problem.
> >


--~--~---------~--~----~------------~-------~--~----~
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
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to