继承人和后者的麻烦Mogensen的二进制编码的前身

时间:2017-12-27 22:15:19

标签: encoding functional-programming binary lambda-calculus

我想将二进制数添加到我的无类型lambda演算库中,但我坚持使用succpred函数。我使用paper by T. Mogensen中列出的表示形式,虽然其中定义的大多数功能都有效,但succpred会返回错误的结果。

我很确定我的代表权是正确的:

dec bin   De Bruijn       classic
0   0     λλλ3            λa.λb.λc.a
1   1     λλλ13           λa.λb.λc.c a
2   10    λλλ2(13)        λa.λb.λc.b (c a)
3   11    λλλ1(13)        λa.λb.λc.c (c a)
4   100   λλλ2(2(13))     λa.λb.λc.b (b (c a))
5   101   λλλ1(2(13))     λa.λb.λc.c (b (c a))
6   110   λλλ2(1(13))     λa.λb.λc.b (c (c a))
7   111   λλλ1(1(13))     λa.λb.λc.c (c (c a))
8   1000  λλλ2(2(2(13)))  λa.λb.λc.b (b (b (c a)))

元组和投影看起来也很好:

tuple         De Bruijn               classic
[T, F]        λ1(λλ2)(λλ1)            λa.a (λb.λc.b) (λb.λc.c)
[T, F, F]     λ1(λλ2)(λλ1)(λλ1)       λa.a (λb.λc.b) (λb.λc.c) (λb.λc.c)
[T, F, F, T]  λ1(λλ2)(λλ1)(λλ1)(λλ2)  λa.a (λb.λc.b) (λb.λc.c) (λb.λc.c) (λb.λc.b)

πkn  De Bruijn  classic
π12  λ1(λλ2)    λa.a (λb.λc.b)
π22  λ1(λλ1)    λa.a (λb.λc.c)

使用0位(shl0)和1位(shl1)进行调整可以很好地进行测试:

SHL0 ≡ λnbzo.z (n b z o) = λ λ λ λ 2 (4 3 2 1)
SHL1 ≡ λnbzo.o (n b z o) = λ λ λ λ 1 (4 3 2 1)

但依赖于上述条款的succpred不会:

SUCC ≡ λn.π22 (n Z A B) ≡ λ π22 (1 Z A B) where
Z ≡ [ZERO, ONE] // encoded like in the first piece of code
A ≡ λp.p (λnm.[SHL0 n, SHL1 n]) ≡ λ 1 (λ λ [SHL0 2, SHL1 2])
B ≡ λp.p (λnm.[SHL1 n, SHL0 m]) ≡ λ 1 (λ λ [SHL1 2, SHL0 1])

PRED ≡ λn.π22 (n Z A B) ≡ λ π22 (1 Z A B) where
Z ≡ [ZERO, ZERO] // encoded like in the first piece of code
A ≡ λp.p (λnm.[SHL0 n, SHL1 m]) ≡ λ 1 (λ λ [SHL0 2, SHL1 1])
B ≡ λp.p (λnm.[SHL1 n, SHL0 n]) ≡ λ 1 (λ λ [SHL1 2, SHL0 2])

示例结果:

succ 0 = λa.λb.λc.c a / λλλ13 ok
succ 1 = λa.λb.λc.b (b c) / λλλ2(21) wrong, expected λλλ2(13)
succ 2 = λa.λb.λc.c (b (c (λd.λe.λf.e (b d e f)) (λd.λe.λf.f (b d e f)))) / λλλ1(2(1(λλλ2(5321))(λλλ1(5321)))) wrong, expected λλλ1(13)
succ 3 = λa.λb.λc.b (b c) / λλλ2(21) wrong, expected λλλ2(2(13))

pred 1 = λa.λb.λc.b a / λλλ23 wrong-ish, expected λλλ3; it's just a leading zero, but it's stated that those should only be caused by inputs that are powers of 2
pred 2 = λa.λb.λc.c (b c) / λλλ1(21) wrong, expected λλλ13
pred 3 = λa.λb.λc.b (b a) / λλλ2(23) wrong, expected λλλ2(13)
pred 4 = λa.λb.λc.c (b c) / λλλ1(21) wrong, expected λλλ1(13)

我的学期评估员已经过数百个学期的测试,因此我对此非常有信心;我怀疑我误读了某些东西或者是错误的东西。我错过了什么吗?

2 个答案:

答案 0 :(得分:2)

因此,正如ljedrz所​​提到的,我们设法让Morgensen数字在单独的聊天中工作。在这个答案中,我将简要介绍一下它的工作原理。

问题是:“我怀疑我误读了某些东西或者是错误的东西。我错过了什么吗? »

tl; dr:事实证明,与评估顺序相关的一些棘手的事情导致了问题。问题中的Morgensen数字工作。

更长的答案:succ如何运作?

N.B。:以下b_n始终被视为1,与原始论文一样。

Morgensen数字背后的想法是将n = b_n ... b_2 b_1编号为\z.\x_0.\x_1. x_{b_1} ( x_{b_2} (... ( x_{b_n} z ) ...) )。这是非常难以理解的,但如果以这样的方式说明它会变得更加清晰:

  

数字n是一个需要3个参数的术语,并在应用时返回x_{b_1} ( x_{b_2} (... ( x_{b_n} z ) ...) )

嗯,目前还不清楚。如果我们更深入一些,我们会看到一个n数字x_0递归地应用x_1z,从术语b_n b_{n-1} ... b_2 b_1开始。请注意,递归调用是“从左到右”,即如果我有一个数字b_n z,那么递归调用将按以下顺序进行评估:

  • 首先i_{n-1},让它为b_{n-1} i_{n-1}
  • 然后i_{n-2},让它为fold
  • ...
  • 最后是i_1 b_1

(好吧,评估策略决定了确切的评估顺序,我认为很容易认为它的评估是这样的)

与列表上的fold_left功能的关系

实际上,当我意识到这一点时,它只是让我想到了一个位列表的l = [b_n; ... ; b_2; b_1]函数:假设你有一个位列表fold_left (fun prev_acc -> fun b -> if b = 0 then x_0 prev_acc else x_1 prev_acc) z l ,那么你可以执行以下操作:

f

fun prev_acc -> fun b -> if b = 0 then x_0 prev_acc else x_1 prev_acc 成为

f (f (... (f z b_n) ...) b_2) b_1

返回(根据Ocaml doc

f z b_n

评估为:

  • x_{b_n} z评估为i_{n-1},即f i_{1} b_1,如上所述。
  • ...
  • fold_left,如上所述。

结论,您绝对可以将Morgensen数字视为列表中的fold_right(或succ,具体取决于您对列表的想象)。

获取数字的succ

获取某个号码的n+1正在获得m = bn ... bi bj bk ... b1。二进制增量作为一个不错的属性:

  

如果bj 0成为第一个bk = ... = b1 = 1(即m + 1 = bn ... bi 1 0 ... 0),那么bn ... bi 0 1 1 1 1 1

这可以说明:

1

如果我添加bn ... bi 0 1 1 1 1 1 +1 -------------------------- bn ... bi 0 1 1 1 1 0 +1 < I have a carry here, which gets propagated ... -------------------------- bn ... bi 0 0 0 0 0 0 +1 < The carry ends up here -------------------------- bn ... bi 1 0 0 0 0 0 < This is our result of doing a +1. ,那么我(通过详细说明所有步骤):

(bn ... bi 0 1 ... 1) + 1

一句好话是要注意(bn ... bi 0) + 1 0 ... 0附加到bj,更一般地说,它也适用于任何(bn ... bi bj 1 ... 1) + 1(bn ... bi bj) + 10 ... 0附加b_n ... bi bj bk ... b1

这似乎相当不错,只有一个问题,进位从右向左传播(LSB到MSB),而Morgensen数字从MSB到LSB。

要解决这个最后一个问题,我们可能是推测性的:假设我有一个数字bn ... bi,我希望有它的继任者。我将不得不以递归方式计算它,但仅从MSB到LSB。

也就是说,如果我在“步骤bj”,我只能使用子序列bjsucc本身。

这使我们可以计算bn ... bi的{​​{1}}。现在是推测部分:

  • 我知道,如果在bj之后,只有1,那么在上一次评论之后,接班人就是((bn ... bi bj) + 1)::(0 ... 0)
  • 但是,如果0中有bk ... b1,则位(bn ... bi bj)保持不变。

因此,我们的想法是在元组中的每个位返回两种可能性。非正式地,传递给fold_left的函数看起来像这样:

fun tuple_msb -> fun bj -> 
(original_msb, incr_msb)

其中(1)tuple_msb是包含(bn ... bi, (bn ... bi) + 1)的元组;根据{{​​1}}计算(2)original_msbincr_msb的位置。事实上:

  • 如果bjbj,则为0
  • 如果(bn ... bi bj) + 1 = (bn ... bi 0) + 1 = (bn ... bi 1)bj,那么1

也就是说,传递给(bn ... bi bj) + 1 = (bn ... bi 1) + 1 = ((bn ... bi) + 1)::0的完整函数如下:

fold_left

基本情况(即起始元素是元组(* We keep the original sequence on the left of the tuple, the incremented `bn ... bi bj` on the right *) fun tuple_msb -> fun bj -> if bj = 0 then (tuple_msb._1 :: 0, tuple_msb._1 :: 1) else (tuple_msb._1 :: 1, tuple_msb._2 :: 0)

从这里开始,很容易回到Morgensen不可读的术语(关于参数的顺序,这里有一个隐藏的小捷径,但它确实无关紧要):

根据(0, 1)和{{1}开头的符号,我们可以将fun tuple_msb -> (tuple_msb._1 :: 0, tuple._1 :: 1)标识为x_0fun tuple_msb -> (tuple_msb._1 :: 1, tuple_msb._2 :: 0)标识为x_1和基本情况(即开头的x_0x_1)。

要获得最终的继承者,我们必须得到返回元组的正确部分,因此最终

z

或以lambda术语:

(0, 1)

相应地定义了所有let succ n = let ret_tuple = n z x_0 x_1 in ret_tuple._2 succ' = λn. π22 (n z x_0 x_1) π22z

我们的x_0与提议的x_1略有不同,即succ'不完全是succx_0并不完全A ,但最后一步很容易,留给感兴趣的读者; - )

答案 1 :(得分:1)

当Bromind告诉我,succ在Ocaml + utop中的定义稍有不同时,我移植了它并用我的库进行了测试。尽管如此,它也失败了,所以我开始分析所涉及的所有术语,只是为了看到一切看起来与我的实现几乎相同。我深信我的评估者都很扎实并且这些定义是有效的,我一直在寻找直到找到真正的原因。

不要过早破坏乐趣,但我会补充说我实际上是联系了教授。 Mogensen关于这一点,他非常友好地为我提供了表达式succ 1的高级评估步骤:

succ 1
 = (λn.π² (n Z A B)) |1|
 = (λn.π² (n Z A B)) (λz01.1 z)
 = π² ((λz01.1 z) Z A B)
 = π² (B Z)
 = π² ((λp.p (λnm.[↑1 n, ↑0 m])) [|0|, |1|])
 = π² ([|0|, |1|] (λnm.[↑1 n, ↑0 m]))
 = π² ((λx.x |0| |1|) (λnm.[↑1 n, ↑0 m]))
 = π² ((λnm.[↑1 n, ↑0 m]) |0| |1|)
 = π² [↑1 |0|, ↑0 |1|]
 = ↑0 |1|
 = (λn.λz01.0 (n z 0 1)) |1|
 = λz01.0 (|1| z 0 1)
 = λz01.0 ((λz01.1 z) z 0 1)
 = λz01.0 (1 z)
 = |2|

这个解决方案是100%有效的,但它与我的实现相比太高了,所以我决定用“低级”β减少来检测定义失败的确切点。

以下是De Bruijn索引表示法中的初始表达式(succ 1):

(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)

及其减少步骤(正常顺序;其他也无效):

(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
 ^          ^                                                                                                       ^^^^^^^
            ↳ becomes λλλ13

(λ1(λλ1))((λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))))
 ^^      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes (λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))

(λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^   ^ ^^^^^^^^^^^^^^^^^
     ↳ becomes λ1(λλλ3)(λλλ13)

(λλ1(λ1(λλλ3)(λλλ13)))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - is dropped

(λ1(λ1(λλλ3)(λλλ13)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^^                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))

(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λ1(λλλ3)(λλλ13))(λλ1)
 ^^                                       ^^^^^^^^^^^^^^^^^
  ↳ becomes λ1(λλλ3)(λλλ13)

(λ1(λλλ3)(λλλ13))(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλ1)
 ^^              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)

(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1)
 ^                                    ^^^^^^ - is dropped

(λλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ13)(λλ1)
 ^                ^                  ^^^^^^^
                  ↳ becomes λλλ13

(λ1((λλλλ1(4321))(λλλ13))((λλλλ2(4321))1))(λλ1)
 ^^                                    ^
  ↳ becomes (λλ1)                      ↳ becomes (λλ1)

(λλ1)((λλλλ1(4321))(λλλ13))((λλλλ2(4321))(λλ1))
 ^   ^^^^^^^^^^^^^^^^^^^^^^ - is dropped

(λ1)((λλλλ2(4321))(λλ1))
 ^^ ^^^^^^^^^^^^^^^^^^^^
  ↳ becomes (λλλλ2(4321))(λλ1)

(λλλλ2(4321))(λλ1)
 ^     ^     ^^^^^
       ↳ becomes λλ1

λλλ2((λλ1)321)
      ^   ^ - is dropped

λλλ2((λ1)21)
      ^^ ^
       ↳ becomes 2

λλλ2(21) // fail

这是一个微妙的问题:AB都需要将其元组设置为非标准化/结构化形式,以便succpred定义可以使用纯β-降低评估者。我注意到了这一点,因为我的实现使用了标准化元组,而Bromind则没有。

让我们看看正确的定义:

(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)

及其减少步骤(再次正常顺序):

(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
 ^          ^                                                                                                                   ^^^^^^^
            ↳ becomes λλλ13

(λ1(λλ1))((λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))))
 ^^      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes (λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))

(λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^   ^ ^^^^^^^^^^^^^^^^^
     ↳ becomes λ1(λλλ3)(λλλ13)

(λλ1(λ1(λλλ3)(λλλ13)))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - is dropped

(λ1(λ1(λλλ3)(λλλ13)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
 ^^                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))

(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λ1(λλλ3)(λλλ13))(λλ1)
 ^^                                             ^^^^^^^^^^^^^^^^^
  ↳ becomes λ1(λλλ3)(λλλ13)

(λ1(λλλ3)(λλλ13))(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλ1)
 ^^              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)

(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1)
 ^                       ^                  ^^^^^^
                         ↳ becomes λλλ3

(λ(λλλ132)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))1))(λλλ13)(λλ1)
 ^                                           ^  ^^^^^^^
                                             ↳ becomes λλλ13

(λλλ132)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13))(λλ1)
 ^   ^  ^^^^^^^^^^^^^^^^^^^^^
     ↳ becomes (λλλλ1(4321))(λλλ3)

(λλ1((λλλλ1(4321))(λλλ3))2)((λλλλ2(4321))(λλλ13))(λλ1)
 ^                       ^ ^^^^^^^^^^^^^^^^^^^^^^
                         ↳ becomes (λλλλ2(4321))(λλλ13)

(λ1((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13)))(λλ1)
 ^^                                            ^^^^^
  ↳ becomes λλ1

(λλ1)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13))
 ^   ^^^^^^^^^^^^^^^^^^^^^ - is dropped

(λ1)((λλλλ2(4321))(λλλ13))
 ^^ ^^^^^^^^^^^^^^^^^^^^^^
  ↳ becomes (λλλλ2(4321))(λλλ13)

(λλλλ2(4321))(λλλ13)
 ^     ^     ^^^^^^^
       ↳ becomes λλλ13

λλλ2((λλλ13)321)
      ^   ^ ^
          ↳ becomes the right-hand side 3 and gets an index upgrade due to extra abstractions

λλλ2((λλ15)21)
      ^  ^
         ↳ gets its index downgraded

λλλ2((λ14)1)
      ^^  ^
       ↳ becomes the right-hand side 1; 4 gets an index downgrade

λλλ2(13) // aww yiss

关键的区别如下:

(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1) // normalized tuple
 ^                                    ^^^^^^ - is dropped

(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1) // unnormalized tuple
 ^                       ^                  ^^^^^^
                         ↳ becomes λλλ3

如果AB包含标准化元组,Z元组中的零点将被删除并退出游戏,因此关键步骤π² ((λnm.[↑1 n, ↑0 m]) |0| |1|) => π² [↑1 |0|, ↑0 |1|]不能完全发生了。

以下是Z的错误(对于纯β减少评估者)和正确的完整(包括ABsucc)定义之间的差异:< / p>

λ PI22 (1 [ZERO, ONE] (λ 1 (λ λ        [SHL0 2,  SHL1 2])) (λ 1 (λ λ        [SHL1 2,  SHL0 1]))) // wrong
λ PI22 (1 [ZERO, ONE] (λ 1 (λ λ TUPLE2 (SHL0 2) (SHL1 2))) (λ 1 (λ λ TUPLE2 (SHL1 2) (SHL0 1)))) // right
    where TUPLE2 ≡ λ λ λ 1 3 2