关于Clojure:教堂数字的阶乘函数

factorial function for Church numerals

我正在尝试实现Lambda微积分,组合器和函数式编程

一书中所述的阶乘lambda表达式。

描述方式为:

1
2
fact = (Y)λf.λn.(((is-zero)n)one)((multiply)n)(f)(predecessor)n
Y = λy.(λx.(y)(x)x)λx.(y)(x)x

其中

1
2
3
(x)y is equivalent to (x y) and
(x)(y)z is equivalent to (x (y x)) and
λx.x is equivalent to (fn [x] x)

为标准教堂数字定义了

is-zeroonemultiplypredecessor。实际定义在这里。

我将其翻译为以下

1
2
3
4
5
(defn Y-mine [y]        ;  (defn Y-rosetta [y]              
  ((fn [x] (y (x x)))   ;    ((fn [f] (f f))                
    (fn [x]             ;     (fn [f]                      
      (y                ;       (y (fn [& args]            
        (x x)))))       ;            (apply (f f) args))))))

1
2
3
4
5
6
(def fac-mine                                ; (def fac-rosetta
  (fn [f]                                    ;      (fn [f]
    (fn [n]                                  ;        (fn [n]
      (is-zero n                             ;          (if (zero? n)
        one                                  ;            1
        (multiply n (f (predecessor n))))))) ;            (* n (f (dec n)))))))

注释掉的版本是Rosetta代码中的等效fac和Y函数。

问题1:

我从其他地方的阅读中了解到,Y-rosettaβ减小为Y-mine。在那种情况下,为什么最好使用一个而不是另一个?

问题2:

即使我使用Y-rosetta。我尝试

时收到StackOverflowError

1
((Y-rosetta fac-mine) two)

while

1
((Y-rosetta fac-rosetta) 2)

工作正常。

无保护的递归在哪里发生?

我怀疑if表单在clojure中的工作方式与我的is-zero实现并不完全相同。但是我自己却找不到错误。

谢谢。

更新:

考虑到@amalloy的回答,我略微更改了fac-mine以采用惰性参数。我对clojure不太熟悉,所以这可能不是正确的方法。但是,基本上,我使is-zero接受匿名零参数函数并评估返回的值。

1
2
3
4
5
6
7
8
9
10
11
(def lazy-one (fn [] one))
(defn lazy-next-term [n f]
  (fn []
    (multiply n (f (predecessor n)))))

(def fac-mine                      
  (fn [f]                          
    (fn [n]                        
      ((is-zero n                  
        lazy-one                    
        (lazy-next-term n f))))))

我现在收到一条错误消息:

1
2
=> ((Y-rosetta fac-mine) two)
ArityException Wrong number of args (1) passed to: core$lazy-next-term$fn  clojure.lang.AFn.throwArity (AFn.java:437)

考虑到lazy-next-term总是用nf

调用,这似乎真的很奇怪


fac-mine的主体看起来不正确:它正在调用(is-zero n one)以产生副作用,然后无条件地调用(multiply n (f (predecessor n)))。大概您希望在这里有条件的选择(尽管鉴于您对is-zero的定义,我不明白为什么它不会引发arity异常)。