【问题标题】:fix-point combinators in clojureclojure 中的定点组合器
【发布时间】:2020-05-02 19:01:19
【问题描述】:

我最喜欢的一种测试我正在学习的语言能力的方法是尝试并实现各种定点组合器。因为我正在学习 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 我更倾向于认为这是对 Wikipedia's notation 的误读:

(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

谁能解释一下

  1. 为什么 U 和 theta-v 的这些实现不起作用;和
  2. 如何解决?

【问题讨论】:

  • 请附上您要翻译的代码的链接。
  • 呃,@amalloy 我翻译的是数学而不是代码,但如果有帮助,当然
  • FWIW 很多数学和代码都是一样的。当然 lambda 演算两者兼而有之。
  • 小点:因为(*)1,你不需要1作为函数!的特例。 (defn ! [n] (apply * (map inc (range n)))) 有效,(defn ! [n] (reduce * (range 1 (inc n)))) 也有效,我更喜欢。

标签: clojure fixpoint-combinators


【解决方案1】:

您对 theta-v 的定义是错误的,原因有两个。第一个非常明显:您接受f 作为参数然后忽略它。更忠实的翻译是使用def 样式,就像您对其他功能一样:

(def theta-v
  "Turing fixed-point combinator by-value"
  (let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))]
    (A A)))

第二个原因有点狡猾:您将λz.xxyz 翻译成(fn [z] ((x x) y) z),记住lisps 需要更多括号来表示函数调用,这些函数调用隐含在lambda 演算符号中。但是,您错过了一组:就像x x y z 的意思是“评估x 两次,然后y 一次,然后最后返回z”,您写的意思是“评估((x x) y),然后扔掉那个结果并返回z"。添加额外的一组括号会产生一个有效的theta-v

(def theta-v
  "Turing fixed-point combinator by-value"
  (let [A (fn [x] (fn [y] (y (fn [z] (((x x) y) z)))))]
    (A A)))

我们可以通过计算一些阶乘来证明它是有效的:

user> (map (theta-v !') (range 10))
(1 1 2 6 24 120 720 5040 40320 362880)

至于 U:要使用 U 组合器,被组合的函数必须改变它们自调用的方式,这意味着您还需要重写 !'

(def U [f] (f f))

(def ! (U (fn [f]
            (fn [n]
              (if (zero? n)
                1
                (* n ((f f) (dec n))))))))

请注意,我已将 (f (dec n)) 更改为 ((f f) (dec n))

【讨论】:

  • 确认!我不敢相信我错过了... theta 以 lambda.x 开头,而不是 lambda.f。有趣的是,U 需要对其参数进行额外修改。 (PS 同意代码/数学的事情,只是想澄清一下我是从 lambda 演算而不是某人的 python 代码或其他东西翻译的)
  • 感谢您的文章。不幸的是,即使对 theta-v 进行了更正,我还是得到了(map (fix/theta-v fix/fib') (range 10)) ; (0 1 1 3 5 7 9 11 13 15) (map fix/fib (range 10)) ; (0 1 1 2 3 5 8 13 21 34) (map (fix/theta-v fix/!') (range 10)) ; (1 0 2 6 12 20 30 42 56 72) (map fix/! (range 10)) ; (1 1 2 6 24 120 720 5040 40320 362880) (希望换行符有点明显)
  • 确实,这看起来肯定是错误的。我没有一个好的答案:看起来您的!' 是正确的,并且theta-v 的实现与维基百科的匹配。因为即使对于小到 1 的值也是错误的,您可以尝试手动评估它,看看哪里出了问题(究竟是从哪里来的零?)。
  • 同意,目前没有时间。好战术。
  • 啊,我明白了。您的theta-v 中没有足够的括号。在 Haskell 或 lambda 演算中,((x x) y) zx x y z 相同,或者在一般情况下,(((x x) y) z) 相同。然而,在大多数 lisps 中,前者的意思是“评估 ((x x) y),然后将其丢弃并返回 z
猜你喜欢
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多