Question

I can't solve this problem from the 4clojure site and the errors are not helping much:

;;Write an oscillating iterate: a function that takes an initial value and a
;;variable number of functions. It should return a lazy sequence of the functions
;;applied to the value in order, restarting from the first function after it hits the end.

(fn osc [value & funs]
  (loop [value value
         funs (cycle funs)]
    (cons value (lazy-seq
                 (recur ((first funs) value) (drop 1 funs))))))

This version of the function shows this error:

java.lang.IllegalArgumentException: Mismatched argument count to recur,
expected: 0 args, got: 2, compiling:(NO_SOURCE_PATH:0)

Why is recur expecting 0 arguments, however I tried tried this other function:

(fn osc [value & funs]
  (let [value value
         funs (cycle funs)]
    (cons value (lazy-seq
                 (osc ((first funs) value) (drop 1 funs))))))

But it yiels this:

java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.IFn

The only place I can think of where the error is after the lazy-seq function it seems to be ok syntactically.

Both functions fail on this test

(= (take 3 (__ 3.14 int double)) [3.14 3 3.0])
Was it helpful?

Solution

The first attempt doesn't work as the lazy-seq macro calls the body you give it when needed, from a zero-arity fn. So even though your code is in the tail position, it's in the tail position of another function. Gotta love macros. I recommend running (source lazy-seq) to grok it personally.

(defn osc-seq [val fns]
  (cons val (lazy-seq (osc-seq ((first fns) val) (rest fns)))))

(defn osc [val & fns]
  (osc-seq val (cycle fns)))

Your second attempt has problems due to repeatedly calling cycle and so (first fns) isn't a fn the second time - it's a seq.

OTHER TIPS

  • Your first example goes wrong because recur is inside a cons - not in tail position (wrong - see edit below). The error message is misleading.
  • The second goes wrong because you apply cycle to your funs on every iteration.

I don't want to answer the question for you, so ...

Consider that if you could call your second function like this:

(osc v (cycle funs))

... you wouldn't call cycle inside it. So construct a function like that, make it local to your apparent function, and call it appropriately.


@pete23 is right. In your first example, the lazy-seq, not the cons, is first in line to spoil tail position for the recur.

The macro lazy-seq wraps its form argument as a parameterless function, in this case presenting the translator with

(fn [] (recur ((first funs) value) (drop 1 funs)))

... in which, as the error message says, the recur is supplying 2 arguments when 0 are expected. As this fooled me, and may fool others, I'll leave the edit trail intact.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top