我最喜欢的测试我正在学习的语言能力的方法之一是尝试并实现各种定点组合器。因为我正在学习 Clojure(虽然我对 lisps 并不陌生),所以我也做了同样的事情。
首先,一些“可测试”的代码,阶乘:
(def !'
"un-fixed factorial function"
(fn [f]
(fn [n]
(if (zero? n)
1
(* n (f (dec n)))))))
(defn !
"factorial"
[n]
(if (zero? n)
1
(apply * (map inc (range n)))))
对于我实现的任何组合器c
,我想验证它((c !') n)
是否等于(! n)
.
我们从传统的 Y 开始:
(defn Y
"pure lazy Y combinator => stack overflow"
[f]
(let [A (fn [x] (f (x x)))]
(A A)))
但是当然 Clojure 并没有那么懒惰,所以我们转向 Z:
(defn Z
"strict fixed-point combinator"
[f]
(let [A (fn [x] (f (fn [v] ((x x) v))))]
(A A)))
事实上,它认为(= ((Z !') n) (! n))
.
现在是我的问题:我无法让U或 Turing 组合器 (theta-v) 正常工作。我怀疑 U 是语言限制,而 theta-v 我更倾向于认为这是对维基百科符号的误读:
(defn U
"the U combinator => broken???"
[f]
(f f))
(defn theta-v
"Turing fixed-point combinator by-value"
[f]
(let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))]
(A A)))
示例 REPL 体验:
((U !') 5)
;=> Execution error (ClassCastException) at fix/!'$fn (fix.clj:55).
;=> fix$_BANG__SINGLEQUOTE_$fn__180 cannot be cast to java.lang.Number
((theta-v !') 5)
;=> Execution error (ClassCastException) at fix/theta-v$A$fn (fix.clj:36).
;=> java.lang.Long cannot be cast to clojure.lang.IFn
谁能解释
- 为什么 U 和 theta-v 的这些实现不起作用;和
- 如何修复它们?