Reputation: 7529
I'm trying to learn about loop/recur
. I wanted to pass the return vector from a function back to the loop and I've tried something like this:
(defn foo [x y]
[(dec x) y])
(loop [x 3 y 4]
(if (> x 0)
(do
(prn x y)
(recur (foo x y)))))
That gives:
1. Caused by java.lang.IllegalArgumentException
Mismatched argument count to recur, expected: 2 args, got: 1
Now I can change the loop parameters to this other form which works:
(defn foo [x y]
[(dec x) y])
(loop [[x y] [3 4]]
(if (> x 0)
(do
(prn x y)
(recur (foo x y)))))
I'd like to know if there's any way I could change the first code to leave the (loop [x 3 y 4] ...)
but change the arguments passed to recur
somehow. I guess I need something like the apply
function, but I couldn't get that to work with recur
, because recur
is not a function.
Upvotes: 1
Views: 110
Reputation: 17859
you could also make up a simple macro for that purpose.. maybe like this (which is basically a macro rewrite of your vectorized params variant):
(defmacro loop1 [bindings & body]
(let [left-side (vec (take-nth 2 bindings))
right-side (vec (take-nth 2 (rest bindings)))]
`(loop [~left-side ~right-side]
~@body)))
(loop1 [x 3 y 4]
(if (> x 0)
(do
(prn x y)
(recur (foo x y)))))
;;=> 3 4
;; 2 4
;; 1 4
nil
so, if you have repetitive usage pattern alike that, this macro could be a nice utility.
Upvotes: 2
Reputation: 37008
There is no easy way around it. recur
is a special
form and Clojures
way around the JVM not having easy access to TCO.
Therefor you can not use apply
here (which would be a solution to call
a function with arguments from a list).
So you have to hold on to the result of foo
and then recur
with
those. Note beside: if
with just one branch is just when
.
(defn foo [x y]
[(dec x) y])
(loop [x 3 y 4]
(when (> x 0)
(prn x y)
(let [[x' y'] (foo x y)]
(recur x' y'))))
If your domain here really is [x y]
(e.g. coordinates) my suggestion
would be to build your functions around that and don't jump between
sometimes using a vector and sometimes passing x/y.
Upvotes: 6