Clojure - using recur vs plain recursive function call

Why does Clojure have a special "recur" shape?

When I replace "recur" with the function itself, I get the same result:

(defn print-down-from [x] (when (pos? x) (println x) (recur (dec x)) ) ) (print-down-from 5) 

It has the same result as

 (defn print-down-from [x] (when (pos? x) (println x) (print-down-from (dec x)) ) ) (print-down-from 5) 

I was wondering if "recur" is just a safety measure so that the compiler throws an error if the developer uses irregular recursion. Or maybe the compiler needs to optimize tail recursion?

But what interests me most is for any other reason than consuming a stack.

+5
source share
2 answers

As explained in the clojure.org functional programming page :

In the absence of mutable local variables, the loop and iteration should have a different form than in languages โ€‹โ€‹with built-in for or while constructs, which are controlled by a state change. In functional languages, loop and iteration are replaced / implemented through recursive function calls. Many of these languages โ€‹โ€‹ensure that function calls made at the tail position do not consume stack space, and thus recursive loops use constant space. Since Clojure uses the Java calling conventions, it cannot and does not fulfill the same guarantees for optimizing calls. Instead, it provides a special recur statement that executes a recursive loop with a constant space, iterating over and moving on to the nearest loop or function block. Although not as common as tail call optimization, it allows you to use most of the same elegant designs and offers the advantage of verifying that calls for repetition can only occur in tail position.

If you are not using recur (or trampoline ), your function calls consume the stack: Thus, if you started (print-down-from 100000) , you would quickly notice a failure.


Providing this language tool provides the following benefits:

  • Unlike regular recursive calls (as indicated in the sample question), using recur does not use the stack.
  • The author knows that TCO is being used (and the stack is not being consumed), since use in a position where TCO is not possible will cause a compile-time failure. Thus, the characteristics of the consumption of the stack are obvious both for the author of the code and for its readers, in contrast to languages โ€‹โ€‹with only automatic TCO (where it would be necessary to read carefully - taking into account macros) to determine whether the call is really before how to find out if it is optimized).
  • Compatibility is maintained with the usual JVM calling conventions, and thus compatibility with code written in other JVM-oriented languages โ€‹โ€‹is maintained.

Finally, for the background, see one of several other StackOverflow related questions:

+10
source

As I understand it, loop .. recur uses tail recursion, so your program didnโ€™t blow the stack, and regular recursion didnโ€™t. Some solutions to problems on 4clojure exit without using loop .. recur , because - I accept a reasonable assumption - the solution can only be deduced using a direct, recursive function call instead of loop .. recur .

From what I read, in some of the Clojure books from a few years ago, you are free to use loop .. recur .

However, at least from the discussion in those books that I read, and from the answers I received to my Clojure questions here in SO, there is some general agreement to try to solve your problem first, using constructs like map , If this is not possible, then be sure to use loop .. recur , and if you do not think it is possible to blow your stack, call direct recursion.

0
source

Source: https://habr.com/ru/post/1237419/


All Articles