Haskell 基于本原递归的二元自然数加法
给定二进制自然数,零表示“两次”表示和“两次加一”表示。如何使用原语递归(仅使用函数Haskell 基于本原递归的二元自然数加法,haskell,Haskell,给定二进制自然数,零表示“两次”表示和“两次加一”表示。如何使用原语递归(仅使用函数foldBNat)表示加法 --zero | n*2 | n*2+1 数据BNat=Z | T BNat | TI BNat 派生(显示) foldBNat::BNat->t->(BNat->t->t)->(BNat->t->t)->t foldBNat n z t ti= 案例n Z->Z TM->TM(折叠NAT m z T ti) TI-m->TI-m(折叠式m-z-t-TI) div2::BNat->BN
foldBNat
)表示加法
--zero | n*2 | n*2+1
数据BNat=Z | T BNat | TI BNat
派生(显示)
foldBNat::BNat->t->(BNat->t->t)->(BNat->t->t)->t
foldBNat n z t ti=
案例n
Z->Z
TM->TM(折叠NAT m z T ti)
TI-m->TI-m(折叠式m-z-t-TI)
div2::BNat->BNat
div2 n=foldBNat n Z(\m\uuUm->m)(\m\uUm->m)
pred::BNat->BNat
pred n=foldBNat n Z(\\ur->TI r)(\m\ut->m)
成功::BNat->BNat
succn=foldbnatn(tiz)(\m\u->tim)(\\ur->tr)
想法:要计算a+b
,我们需要增加b
a
次。因此:
0 + b = b
1 + b = succ b
2 + b = succ (succ b)
3 + b = succ (succ (succ b))
...
incBy 0 = id
incBy 1 = succ
incBy 2 = succ . succ
incBy 3 = succ . succ . succ
...
我们可以从写作开始
plus a b = foldBNat a b (\m r -> ...
但是在这里我们被卡住了:m
代表a
的一半(因为这里a=tm
,即a=2*m
),而r
是b
m
次递增的结果(即m+b
)。我们对此无能为力。我们想要的是a+b=2*m+b
,这是我们无法直接从m+b
获得的。应用T
只会给我们2*(m+b)=2*m+2*b
,这太大了,根据规则,我们不能直接递归plus
来计算m+(m+b)=2*m+b
我们需要的是一种更直接的方式来操作succ
操作的数量
想法:不要直接计算一个数字;而是计算一个函数(将其参数递增一定次数)。因此:
我们可以直接实施:
incBy :: BNat -> (BNat -> BNat)
incBy n = foldBNat n id (\_ r -> r . r) (\_ r -> succ . r . r)
这里是r。r
给我们提供了一个函数,该函数将一个数字的增量增加到r
的两倍(通过应用r
两次)
现在我们可以简单地将加法定义为:
plus :: BNat -> BNat -> BNat
plus n m = (incBy n) m
(这恰好是多余的,因为plus=incBy
)。谢谢,回答得很好!我希望有一种更有效的加法方法,但我想这里的问题是原始递归?结构递归会允许更有效的算法吗?一般递归呢?回答我自己的评论,使用结构/一般递归可以做得更好。