Haskell 解读addC码和进位码

Haskell 解读addC码和进位码,haskell,Haskell,好的,我在Haskell中有这样的代码: data Bigit = O | I deriving (Show,Eq) add x y = reverse $ addC O (reverse x) (reverse y) addC O [] [] = [] addC I [] [] = [I] addC carry [] r = addC carry [O] r addC carry l [] = addC carry l [O] addC carry (left:leftOver) (rig

好的,我在Haskell中有这样的代码:

data Bigit = O | I deriving (Show,Eq)

add x y = reverse $ addC O (reverse x) (reverse y)

addC O [] [] = []
addC I [] [] = [I]
addC carry [] r = addC carry [O] r
addC carry l [] = addC carry l [O]
addC carry (left:leftOver) (right:rightOver) = sumBigit :(addC newCarry leftOver    
                                                                             rightOver)
where
    (sumBigit,newCarry)
        = case (left,right,left) of
            (O,O,O) -> (O,O)
            (O,I,O) -> (I,O)
            (I,O,O) -> (I,O)
            (I,I,O) -> (O,I)
            (O,O,I) -> (I,O)
            (O,I,I) -> (O,I)
            (I,O,I) -> (O,I)
            (I,I,I) -> (I,I)
我需要弄清楚这意味着什么。到目前为止,我知道它使用bigits和bigits列表作为类型,bigit是I(表示1)和O(表示0)

我发现add和addC的类型签名:

add :: [Bigit] -> [Bigit] -> [Bigit]
addC :: Bigit -> [Bigit] -> [Bigit] -> [Bigit]
为了帮助我理解,我已经将代码加载到GHCI中,并且一直在使用它。例如,我知道如果我告诉它:

add [I,O] [I,O]
它给了我[I,I,O],因为它如下:

reverse (addC O (reverse x) (reverse y))
reverse (addC O [O,I] [O,I])
但是,从这里开始,我对如何着手解决
addC
部分感到困惑。我有正确的论点:一个Bigit和两个Bigit列表。然而,我不明白这和什么模式相匹配。我很不明白“携带”是什么意思。
任何人都可以尝试帮助吗?

正如注释中所解释的那样,
addC
函数使用反向二进制代码(没有真正的原因,其位被命名为Bigits),并且有一个bug,需要在
案例中包含进位。
addC
的许多变体涵盖了所有可能的输入组合,特别是在递归调用中:

addC O [] [] = []
在这种情况下,我们已经用完了数字,进位输入为零。这意味着我们不需要再添加一个数字,可以返回一个空列表

addC I [] [] = [I]
在这里,当输入项用完时,我们还有一个进位,所以我们用一个数字扩展结果。一旦两个列表都用尽,这两种情况中的任何一种都将匹配,并终止递归计算,因为它们不再调用addC

addC carry [] r = addC carry [O] r
这是用来加宽左项的,因为右项没有用尽(如果是的话,早期的模式会匹配它)

同样,当左项未用尽时,扩大右项

使用所有这些模式,可以保证主addC定义使用的列表长度相等,并且不会在长度溢出中丢失数据。它可以用不同的方式书写,这样我们就可以复制任意一个术语的剩余部分,一旦进位为O,另一个术语为[],但模式是详尽的和终止的,这是最重要的。附带说明的是,就这个加法器而言,[]是一个有效的零值

addC carry (left:leftOver) (right:rightOver) = 
     sumBigit :(addC newCarry leftOver rightOver)
     where (sumBigit,newCarry) = ....
这是功能的核心。它从每个项(左、右)中提取一个Bigit,并使用真值表从这些项和进位中计算两位和(如果不是bug,它会的)。结果保存该和的最低有效位,然后使用新的进位值对其余两项进行递归求和

作为练习,我冒昧地使用
foldr
编写了相同的概念。结果不是很好,但确实避免了反转步骤;相反,排列不同长度的列表需要一个单独的扩展步骤,我是通过测量列表的长度来完成的

extMatch :: a -> b -> [a] -> [b] -> [(a,b)]
extMatch a0 b0 a b = zip (ext a0 (lb-la) a) (ext b0 (la-lb) b)
  where ext x0 l x | l>0 = concat [replicate l x0, x]
                   | l<=0 = x
        la = length a
        lb = length b

add2 :: [Bigit] -> [Bigit] -> [Bigit]
add2 x y = extsum $ foldr addC2 (O, []) (extMatch O O x y)
  where extsum (O,sum) = sum
        extsum (I,sum) = I:sum

addC2 :: (Bigit, Bigit) -> (Bigit, [Bigit]) -> (Bigit, [Bigit])
addC2 (O, O) (O, sumbits) = (O, O:sumbits)
addC2 (O, O) (I, sumbits) = (O, I:sumbits)
addC2 (O, I) (O, sumbits) = (O, I:sumbits)
addC2 (O, I) (I, sumbits) = (I, O:sumbits)
addC2 (I, O) (O, sumbits) = (O, I:sumbits)
addC2 (I, O) (I, sumbits) = (I, O:sumbits)
addC2 (I, I) (O, sumbits) = (I, O:sumbits)
addC2 (I, I) (I, sumbits) = (I, I:sumbits)
extMatch::a->b->[a]->[b]->[(a,b)]
extMatch a0 b0 a b=拉链(ext a0(磅-磅-磅-磅)a)(ext b0(磅-磅)b)
其中ext x0 l x | l>0=concat[复制l x0,x]
|l[Bigit]->[Bigit]
add2 x y=extsum$foldr addC2(O,[])(extMatch O x y)
其中extsum(O,sum)=sum
extsum(I,sum)=I:sum
addC2::(Bigit,Bigit)->(Bigit,[Bigit])->(Bigit,[Bigit])
addC2(O,O)(O,sumbits)=(O,O:sumbits)
addC2(O,O)(I,sumbits)=(O,I:sumbits)
addC2(O,I)(O,sumbits)=(O,I:sumbits)
addC2(O,I)(I,sumbits)=(I,O:sumbits)
addC2(I,O)(O,sumbits)=(O,I:sumbits)
addC2(I,O)(I,sumbits)=(I,O:sumbits)
addC2(I,I)(O,sumbits)=(I,O:sumbits)
addC2(I,I)(I,sumbits)=(I,I:sumbits)

addC
函数实现一个涟漪进位加法器,case语句只是一个全加器。你需要学习二进制算术来理解代码,一旦你这样做,它几乎是微不足道的。哦,代码是错误的。case语句中的
应该是
进位
。不管是哪一个。请注意,代码实际上是有缺陷的,因为它使用了两次
left
,而
carry
根本没有。因此,
add[I,O][I,O]
给出了错误的结果(显然2+2实际上不是5--1984不能承受的)。看起来很好地描述了augustss说的话是的。。很抱歉当我把它和我的床单比较时,我发现了这一点。因此,如果我有addC O[O,i][O,i],它会是:addC carry(O:[i])(O:[i])=sumBigit:(addC newCarry levert rightOver),在这种情况下,它会给我addC O(O:[i])(O:[i])=O:(addC O[i][i])
extMatch :: a -> b -> [a] -> [b] -> [(a,b)]
extMatch a0 b0 a b = zip (ext a0 (lb-la) a) (ext b0 (la-lb) b)
  where ext x0 l x | l>0 = concat [replicate l x0, x]
                   | l<=0 = x
        la = length a
        lb = length b

add2 :: [Bigit] -> [Bigit] -> [Bigit]
add2 x y = extsum $ foldr addC2 (O, []) (extMatch O O x y)
  where extsum (O,sum) = sum
        extsum (I,sum) = I:sum

addC2 :: (Bigit, Bigit) -> (Bigit, [Bigit]) -> (Bigit, [Bigit])
addC2 (O, O) (O, sumbits) = (O, O:sumbits)
addC2 (O, O) (I, sumbits) = (O, I:sumbits)
addC2 (O, I) (O, sumbits) = (O, I:sumbits)
addC2 (O, I) (I, sumbits) = (I, O:sumbits)
addC2 (I, O) (O, sumbits) = (O, I:sumbits)
addC2 (I, O) (I, sumbits) = (I, O:sumbits)
addC2 (I, I) (O, sumbits) = (I, O:sumbits)
addC2 (I, I) (I, sumbits) = (I, I:sumbits)