我想将二进制数添加到我的无类型lambda演算库中,但我坚持使用succ
和pred
函数。我使用paper by T. Mogensen中列出的表示形式,虽然其中定义的大多数功能都有效,但succ
和pred
会返回错误的结果。
我很确定我的代表权是正确的:
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)
但依赖于上述条款的succ
和pred
不会:
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)
我的学期评估员已经过数百个学期的测试,因此我对此非常有信心;我怀疑我误读了某些东西或者是错误的东西。我错过了什么吗?
答案 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_1
或z
,从术语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
(好吧,评估策略决定了确切的评估顺序,我认为很容易认为它的评估是这样的)
与列表上的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) + 1
是0 ... 0
附加b_n ... bi bj bk ... b1
。
这似乎相当不错,只有一个问题,进位从右向左传播(LSB到MSB),而Morgensen数字从MSB到LSB。
要解决这个最后一个问题,我们可能是推测性的:假设我有一个数字bn ... bi
,我希望有它的继任者。我将不得不以递归方式计算它,但仅从MSB到LSB。
也就是说,如果我在“步骤bj”,我只能使用子序列bj
和succ
本身。
这使我们可以计算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_msb
和incr_msb
的位置。事实上:
bj
为bj
,则为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_0
,fun tuple_msb -> (tuple_msb._1 :: 1, tuple_msb._2 :: 0)
标识为x_1
和基本情况(即开头的x_0
是x_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)
,π22
和z
。
我们的x_0
与提议的x_1
略有不同,即succ'
不完全是succ
而x_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
这是一个微妙的问题:A
和B
都需要将其元组设置为非标准化/结构化形式,以便succ
和pred
定义可以使用纯β-降低评估者。我注意到了这一点,因为我的实现使用了标准化元组,而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
如果A
或B
包含标准化元组,Z
元组中的零点将被删除并退出游戏,因此关键步骤π² ((λnm.[↑1 n, ↑0 m]) |0| |1|) => π² [↑1 |0|, ↑0 |1|]
不能完全发生了。
以下是Z
的错误(对于纯β减少评估者)和正确的完整(包括A
,B
,succ
)定义之间的差异:< / 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