0

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])
rtruszk
  • 3,902
  • 13
  • 36
  • 53
loki
  • 2,271
  • 5
  • 32
  • 46
  • The second version is missing just one word to be correct: `apply`. Just find the right place to put it... – A. Webb May 08 '14 at 21:50

2 Answers2

2
  • 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.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37
2

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.

pete23
  • 2,204
  • 23
  • 28