解密addC代码并随身携带

时间:2011-11-20 20:34:24

标签: 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) (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]
它给了我[我,我,O],因为它遵循:

reverse (addC O (reverse x) (reverse y))
reverse (addC O [O,I] [O,I])

但是从这里开始,我很困惑如何找出addC部分。我有正确的论点:Bigit和两个Bigits列表。但是,我不明白与之匹配的模式。我对“携带”的含义感到很困惑。 有人可以尝试和帮助吗?

1 个答案:

答案 0 :(得分:1)

正如在评论中所解释的那样,addC函数对反向二进制代码进行操作(没有真正原因的名称为Bigits),并且有一个错误,其中携带需要包含在case中图案。 addC的许多变体都涵盖了所有可能的输入组合,特别是在递归调用中:

addC O [] [] = []

这是我们用完数字的情况,进位输入为零。这意味着我们不需要添加另一个数字并且可以返回空列表。

addC I [] [] = [I]

当我们用完输入项时,我们有一个遗留物,所以我们用一个数字扩展结果。一旦两个列表都用尽,这些情况中的任何一个都会匹配,并终止递归评估,因为它们不会再次调用addC。

addC carry [] r = addC carry [O] r

这用于拓宽左词,因为右词没有用尽(如果是,早期的模式会与之匹配)。

addC carry l [] = addC carry l [O]

同样,在没有用完左期时扩大正确的期限。

使用所有这些模式,可以保证主要的addC定义有相同的长度列表可供使用,并且携带不会在长度溢出中丢失。它本来可以用不同的方式写成,这样我们只是复制了任一项的左边部分,一旦进位是O而另一个术语是[],但这些模式是详尽的并且终止,这是最重要的。附注是,就该加法器而言,[]是有效的零值。

addC carry (left:leftOver) (right:rightOver) = 
     sumBigit :(addC newCarry leftOver rightOver)
     where (sumBigit,newCarry) = ....

这是功能的核心。它从左右各个项中提取一个Bigit,并使用真值表计算这些和进位的两位总和(好吧,如果它没有错误,它会的话)。结果保持该总和的最低有效位,然后保持两个项的其余部分与新进位值的递归和。

作为练习,我冒昧地使用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)