Clojure函数如何扩展?

时间:2019-03-27 01:59:41

标签: clojure

摘自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-cps10identity ...

调用

但是在letfn中,cont[v]被定义为(k (* v n))

等于k = identityn = 10

但是我不明白v的含义和recur的含义。

3 个答案:

答案 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时,kidentity函数。 letfn表单立即创建一个新函数,该函数接受参数v,并在闭包中捕获kidentity)。

然后它检查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等于什么

vcont的自变量,因此在调用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是结果。