Reputation: 2311
I just finished 4clojure Problem 60, here's the code for my first program with the problem description:
;;Write a function which behaves like reduce, but returns each intermediate
;;value of the reduction. Your function must accept either two or three arguments,
;;and the return sequence must be lazy.
(fn red
([fun sq]
(red fun (first sq) (rest sq)))
([fun acum sq]
(if (empty? sq) acum
(cons acum (lazy-seq (red fun (fun acum (first sq)) (rest sq)))))))
The core of the function occurs one line bellow the if, I just return the initial value followed by applying it to the next element in the sequence. But it fails for the second test case involving vectors:
user=> (red conj [1] [2 3 4])
([1] [1 2] [1 2 3] 1 2 3 4);; it should be ([1] [1 2] [1 2 3] [1 2 3 4])
It took me some time to realize that the problem was the cons
which just adds the vector
[1 2 3 4]
as it were the rest of the list instead as a single element.
What I did is to convert cons
to concat
and acum
to [acum]
and it worked:
(fn red
([fun sq]
(red fun (first sq) (rest sq)))
([fun acum sq]
(if (empty? sq) [acum]
(concat [acum] (lazy-seq
(red fun (fun acum (first sq)) (rest sq)))))))
Don't ask me why but it seems to me kind of inelegant, the other solutions didn't use concat
neither.
The question is, considering the first function as it is, what function/macro does the work without modifying the code too much.
Upvotes: 0
Views: 136
Reputation: 1
You can avoid the use of concat if you take cons instead. Cons is lazy (see the discussion of cons vs. conj on Stack Overflow):
(defn my-reductions
([f sq] (my-reductions f (first sq) (rest sq)))
([f init sq]
(if (empty? sq)
(list init)
(cons init (lazy-seq (my-reductions f (f init (first sq)) (rest sq))))
)
)
)
Btw: This code passes the 4Clojure tests, but doesn't quite behave like reductions in all cases:
(my-reductions + [])
(nil)
(reductions + [])
(0)
Upvotes: 0