摘自Clojure的喜悦:
(defn fac-cps [n k]
(letfn [(cont [v] (k (* v n)))]
(if (zero? n)
(k 1)
(recur (dec n) cont))))
(defn fac [n]
(fac-cps n identity))
(fac 10)
3628800
我有兴趣了解上述功能的扩展方式。
fac-cps
用10
和identity
...
但是在letfn中,cont[v]
被定义为(k (* v n))
等于k
= identity
,n
= 10
但是我不明白v
的含义和recur
的含义。
答案 0 :(得分:3)
所有行都相同。我刚刚使用了替换规则:
(fac-cps 10 identity)
(recur 9 (fn [v] (identity (* v 10))))
(recur 8 (fn [v] ((fn [v] (identity (* v 10))) (* v 9))))
(recur 7 (fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))))
(recur 6 (fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))))
(recur 5 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))))
(recur 4 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))))
(recur 3 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))))
(recur 2 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))))
(recur 1 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))) (* v 2))))
(recur 0 (fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))) (* v 2))) (* v 1))))
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))) (* v 2))) (* v 1))) 1)
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))) (* v 2))) (* 1 1))
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* v 3))) (* (* 1 1) 2))
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* v 4))) (* (* (* 1 1) 2) 3))
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* v 5))) (* (* (* (* 1 1) 2) 3) 4))
((fn [v] ((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* v 6))) (* (* (* (* (* 1 1) 2) 3) 4) 5))
((fn [v] ((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* v 7))) (* (* (* (* (* (* 1 1) 2) 3) 4) 5) 6))
((fn [v] ((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* v 8))) (* (* (* (* (* (* (* 1 1) 2) 3) 4) 5) 6) 7))
((fn [v] ((fn [v] (identity (* v 10))) (* v 9))) (* (* (* (* (* (* (* (* 1 1) 2) 3) 4) 5) 6) 7) 8))
((fn [v] (identity (* v 10))) (* (* (* (* (* (* (* (* (* 1 1) 2) 3) 4) 5) 6) 7) 8) 9))
(identity (* (* (* (* (* (* (* (* (* (* 1 1) 2) 3) 4) 5) 6) 7) 8) 9) 10))
(identity 3628800)
; ==> 3628800
答案 1 :(得分:0)
在第一次调用fac-cps
时,k
是identity
函数。 letfn
表单立即创建一个新函数,该函数接受参数v
,并在闭包中捕获k
(identity
)。
然后它检查n
是否为零,这是此递归的基数/终止数。
否则,还有更多工作要做,因此它在递减n
时重复出现,但是现在传递了新的cont
函数,该函数在k
上关闭。这将一遍又一遍,直到n
达到零,从而产生嵌套闭包的盛开洋葱。这就是继续传递样式,这就是cps
中的fac-cps
的意思。与纯递归方法相反,在闭包中捕获的那些值将改为在调用堆栈中。
请注意,直到cont
达到零时,n
中的工作(在所有它们中,彼此嵌套)才完成。在终端情况下调用k
时,该函数可能会包装许多其他函数。换句话说,这正在积聚一个待评估的重击。例如,(fac-cps 3)
最终将评估此功能:
(fn [v] ((fn [v] ((fn [v] (identity (* v 3))) (* v 2))) (* v 1)))
但我不明白v等于什么
v
是cont
的自变量,因此在调用cont
之前不会被知道。
重复出现的内容扩展到
recur
是一种特殊形式,可以“递归”地重新输入该功能,但不占用堆栈。您可以将recur
替换为fac-cps
,但是如果n
足够大,则会导致堆栈溢出。
答案 2 :(得分:0)
此函数通过建立延续(基本上是将来将要执行的功能)来评估阶乘,然后在(fac-cps 0)
处对其进行评估。
例如在(fac-cps 3)
处,延续是“取一个数字,将其乘以3,然后将其传递给上一个延续,这将使它保持单独(identity
)”。
在(fac-cps 2)
,延续是“取一个数字,将其乘以2,然后将其传递给先前的延续,将其乘以2并将其传递给先前的延续,将其保留下来”
在(fac-cps 1)
处,延续是“取一个数字,将其乘以1,然后将其传递给先前的延续,将其乘以2,然后传递给先前的延续,其将其乘以3并将其传递到上一个延续,这将使其保持孤独。”
然后最后在(fac-cps 0)
处给出数字:1 * 3 * 2 * 1
是结果。