4
\$\begingroup\$

I'm a Clojure novice working through 4Clojure problems. In one problem I am supposed to create a function that takes a number and returns that number of Fibonaccis. My first thought was to create a function that creates a lazy sequence of Fibonaccis and then just takes however many it needs. Here is what I came up with:

(fn [numberOfFibs]
 (defn lazyFib [a b]
   (cons a (lazy-seq (lazyFib b (+ b a)))))
 (take numberOfFibs (lazyFib 1 1)))

This did work in my IDE but it wasn't accepted by 4Clojure due to the private defn. defs are not accepted by the website.

How can I refactor this function to remove the defn? And what are the problems with style and readability? Is it easy to reason about the function?

\$\endgroup\$
5
\$\begingroup\$

How can I refactor this function to remove the defn?

What you are looking for is letfn. It can be used to locally define a recursive function or corecursive functions. In this case:

  (fn [numberOfFibs]
    (letfn [(lazyFib [a b] (cons a (lazy-seq (lazyFib b (+ b a)))))]
      (take numberOfFibs (lazyFib 1 1))))

And what are the problems with style and readability?

You shouldn't use nested defs. Behavior is not misleading. Especially it is not scoped in the form it appears and affects the current namespace, as if it was a top level form.

=> lazyFib
CompilerException java.lang.RuntimeException: Unable to resolve symbol: lazyFib in this context, compiling:(NO_SOURCE_PATH:1:42) 
=> (fn [numberOfFibs]
    (defn lazyFib [a b]
      (cons a (lazy-seq (lazyFib b (+ b a)))))
    (take numberOfFibs (lazyFib 1 1)))
#<resistors$eval4432$fn__4433 deneme.resistors$eval4432$fn__4433@49531c8c>
=> lazyFib
#<Unbound Unbound: #'deneme.resistors/lazyFib>
=> ((fn [numberOfFibs]
    (defn lazyFib [a b]
      (cons a (lazy-seq (lazyFib b (+ b a)))))
    (take numberOfFibs (lazyFib 1 1))) 10)
(1 1 2 3 5 8 13 21 34 55)
=> lazyFib
#<resistors$eval4440$fn__4441$lazyFib__4442 deneme.resistors$eval4440$fn__4441$lazyFib__4442@6d17b6ce>

Especially confusing is that just evaluating the fn without calling it pollutes the namespace with an unbound name.

Is it easy to reason about the function?

Other than the nested defn it is very understandable.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Sobieck00, an alternative to (letfn [(lazyFib ...)] ...) is (let [lazyFib (fn [a b] ...)] ...). In this case, I would use letfn, as abuzittin gillifirca, suggests, but when I have to combine a local function definition with other local definitions, sometimes I use let, because it makes the code slightly more concise. This is probably a a matter of personal preference, however. \$\endgroup\$ – Mars Dec 19 '14 at 3:49
  • \$\begingroup\$ Thanks @Mars, that would be an excellent new answer. In the comments, though, it is not nearly visible enough. One minor point to fix in your new answer: In order to define a recursive function in let bindings we'd need a named fn expression as in (let [lazyFib (fn lazyFib [a b] ...)], because left hand side of a binding is not yet available to the right hand side in let. \$\endgroup\$ – abuzittin gillifirca Dec 19 '14 at 7:30
  • \$\begingroup\$ Thanks abuzittin gillifirca--I didn't know that about recursion in a function defined in a let. I don't consider it a new answer, though--only a stylistic variant on your answer. \$\endgroup\$ – Mars Dec 19 '14 at 16:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.