我正在尝试实现如Lambda-calculus, Combinators and Functional Programming
一书中所述的阶乘lambda表达式它描述的方式是:
fact = (Y)λf.λn.(((is-zero)n)one)((multiply)n)(f)(predecessor)n
Y = λy.(λx.(y)(x)x)λx.(y)(x)x
,其中
(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-zero
,one
,multiply
和predecessor
是为标准教堂数字定义的。实际定义here。
我将其翻译成以下
(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))))))
和
(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 code的等效fac和Y函数。
问题1:
我从其他地方的阅读中了解到,Y-rosetta
β减少到Y-mine
。在哪种情况下,为什么最好使用那个呢?
问题2:
即使我使用Y-rosetta
。我尝试
((Y-rosetta fac-mine) two)
,而
((Y-rosetta fac-rosetta) 2)
工作正常。
无人防守的递归发生在哪里?
我怀疑这与if
表单在clojure中的工作方式有关,而这与我的is-zero
实现并不完全等效。但我自己一直无法找到错误。
感谢。
更新
考虑到@ amalloy的回答,我稍微改变了fac-mine
来采取懒惰的论点。我对clojure不是很熟悉,所以这可能不是正确的方法。但是,基本上,我使is-zero
采用匿名零参数函数并评估它返回的任何内容。
(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))))))
我现在收到错误说:
=> ((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
和n
f
,这似乎很奇怪
答案 0 :(得分:1)
fac-mine
的正文看起来不对:它正在调用(is-zero n one)
副作用,然后无条件地调用(multiply n (f (predecessor n)))
。大概你想要一个条件选择(虽然我不明白为什么这不会抛出一个arity异常,给定你is-zero
的定义。)