clojure recur和命令循环 [英] clojure recur vs imperative loop

查看:171
本文介绍了clojure recur和命令循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

学习Clojure并尝试了解实现:



有什么区别:

 (def factorial 
(fn [n]
(loop [cnt n acc 1]
(if(zero?cnt)
acc
(* cnt)(* acc cnt))
;在循环中cnt将取值(dec cnt)
; acc将取值(* acc cnt)
) ))

和以下类似C的伪代码

  function factorial(n)
for(cnt = n,acc = 1){
if(cnt == 0)return acc;
cnt = cnt-1;
acc = acc * cnt;
}
//在循环cnt中将取值(dec cnt)
//并且acc将取值(* acc cnt)

Clojure的循环和recur是专门用于编写简单命令循环的表单吗?
(假设伪代码的for创建它自己的范围,因此cnt和acc仅存在于循环内)

解决方案

< em> Clojure的循环 recur 是专门用于编写简单的命令循环的表单 p>

是的。



功能性术语:




  • 循环是递归的简化形式 tail-recursion

  • 变量不会在循环体中修改。



Clojure的重复对尾部递归调用周围的递归点




  • 它重复使用一个堆栈框架,因此工作更快,避免堆栈
    溢出。

  • 它只能作为任何调用中所做的最后一件事,即所谓的尾位置



每次连续的 recur 调用都会覆盖最后一个。



递归点是




  • a fn

  • 可能伪装在 li> a loop 表单,它还绑定/设置/初始化
    本地/变量。


因此,您的 factorial 函数可以重写

 (def factorial 
(fn [n]
(fn fact [cnt acc]
zero?cnt)
acc
(fact(dec cnt)(* acc cnt))))
n 1)))

...这是更慢,并有风险堆栈溢出。



不是每个C / C ++循环平滑地翻译。你可以从嵌套循环中得到麻烦,其中内部循环修改外部变量中的变量。






顺便说一句, factorial 函数




  • 会很快导致整数溢出。如果你想避免
    这个,使用 1.0 而不是 1 得到浮点数b $ b算术,或使用 *'而不是 * 来获得Clojure的 BigInt
    算术。

  • 会在负参数上循环。



后者的快速修正是

 (def factorial 
(fn [n]
(loop [cnt n acc 1]
(if(pos?cnt)
* acccnt))
acc))))
; 1

...虽然最好是返回 nil Double.NEGATIVE_INFINITY


Learning Clojure and trying to understand the implementation:

What's the difference from:

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          (recur (dec cnt) (* acc cnt))
; in loop cnt will take the value (dec cnt)
; and acc will take the value (* acc cnt)
))))

and the following C-like pseudocode

function factorial (n)
    for( cnt = n,  acc = 1) {
        if (cnt==0) return acc;
        cnt = cnt-1;
        acc = acc*cnt;
    }
// in loop cnt will take the value (dec cnt)
// and acc will take the value (* acc cnt)

Are clojure's "loop" and "recur", forms specifically designed to code a simple imperative loop ? (assuming pseudocode's "for" creates it's own scope, so cnt and acc exists only inside the loop)

解决方案

Are Clojure's loop and recur forms specifically designed to code a simple imperative loop?

Yes.

In functional terms:

  • A loop is a degenerate form of recursion called tail-recursion.
  • The 'variables' are not modified in the body of the loop. Instead, they are re-incarnated whenever the loop is re-entered.

Clojure's recur makes a tail-recursive call to the surrounding recursion point.

  • It re-uses the one stack frame, so working faster and avoiding stack overflow.
  • It can only happen as the last thing to do in any call - in so-called tail position.

Instead of being stacked up, each successive recur call overwrites the last.

A recursion point is

  • a fn form, possibly disguised in defn or letfn OR
  • a loop form, which also binds/sets-up/initialises the locals/variables.

So your factorial function could be re-written

(def factorial
  (fn [n]
    ((fn fact [cnt acc]
      (if (zero? cnt)
        acc
        (fact (dec cnt) (* acc cnt))))
     n 1)))

... which is slower, and risks stack overflow.

Not every C/C++ loop translates smoothly. You can get trouble from nested loops where the inner loop modifies a variable in the outer one.


By the way, your factorial function

  • will cause integer overflow quite quickly. If you want to avoid this, use 1.0 instead of 1 to get floating point (double) arithmetic, or use *' instead of * to get Clojure's BigInt arithmetic.
  • will loop endlessly on a negative argument.

A quick fix for the latter is

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
      (if (pos? cnt)
        (recur (dec cnt) (* acc cnt))
        acc))))
; 1

... though it would be better to return nil or Double.NEGATIVE_INFINITY.

这篇关于clojure recur和命令循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆