Reputation: 137
I'm studying Clojure and Functional Programming, in order to practice, I am working on the 4clojure problems.
this function, (not the best approach.. I know) is working. (Reverse interleave) However, the function is retuning nil.
(defn reverse_interleave
[coll ss]
(let [xx (dec ss)]
(loop [coll (reverse coll) s xx _ss ss ret `()]
(if (nil? (first coll)) (do (println :ret ret) ret))
(when-let [x (first coll)]
(recur
(rest coll)
(if (zero? s) xx (dec s))
(if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
(if (zero? _ss)
(map-indexed #(if (= % s) (cons x %2) %2) ret)
(cons (list x) ret))
))
)) ret)
(reverse_interleave (range 9) 3)
The question is... Why?
Upvotes: 1
Views: 142
Reputation: 3014
It's important to remember here that Clojure doesn't really do statements like most imperative languages. With do
it is possible to evaluate multiple expressions for side effects and then return the value of the last one, and several constructs like let
and fn
contain an implicit do
. But it's not possible for any expression other than the last one to halt evaluation and say "we're done" unless it's throwing an exception. As such, the whole line
(if (nil? (first coll)) (do (println :ret ret) ret))
can only be seen by its side effect. It will evaluate either as the value in ret
or as nil
, and then it will be discarded.
Then we open a when-let
. Because it's a variant of when
, this will either return the value of its body if the conditions are met, or nil
if not. The body is an instruction to recur
to the beginning of the loop, so this loop can only terminate in the value nil
.
The natural fix seems to be to take the last paren of the if
form and move it after the when-let
so that that entire form is the value of the else-case. Your code as it is also fails to compile for me because it uses the name ret
outside the loop which creates it, but with that gone it seems to work as intended:
(defn reverse_interleave
[coll ss]
(let [xx (dec ss)]
(loop [coll (reverse coll) s xx _ss ss ret `()]
(if (nil? (first coll))
(do (println :ret ret) ret)
(when-let [x (first coll)]
(recur
(rest coll)
(if (zero? s) xx (dec s))
(if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
(if (zero? _ss)
(map-indexed #(if (= % s) (cons x %2) %2) ret)
(cons (list x) ret))))))))
(reverse_interleave (range 9) 3)
;;returns ((0 3 6) (1 4 7) (2 5 8))
Upvotes: 2