Adler-32 是一个简单的校验和算法,但这个 JAVA 实现还是非常复杂,很难看清楚位操作之间的关系。
以下是 Adler-32 算法的 Haskell 实现:
-- file: ch04/Adler32.hs
import Data.Char (ord)
import Data.Bits (shiftL, (.&.), (.|.))
base = 65521
adler32 xs = helper 1 0 xs
where helper a b (x:xs) = let a' = (a + (ord x .&. 0xff)) `mod` base
b' = (a' + b) `mod` base
in helper a' b' xs
helper a b [] = (b `shiftL` 16) .|. a
在这段代码里, shiftL 函数实现逻辑左移, (.&.) 实现二进制位的并操作, (.|.) 实现二进制位的或操作, ord 函数则返回给定字符对应的编码值。
helper 函数通过尾递归来进行计算,每次对它的调用,都产生新的累积变量,效果等同于 JAVA 在 for 循环里对变量的赋值更新。当列表处理完毕,递归终止时,程序计算出校验和并将它返回。
和前面抽取出 map 和 filter 函数类似,从 Adler32 函数里面,我们也可以抽取出一种通用的抽象,称之为折叠(fold):它对一个列表中的所有元素做某种处理,并且一边处理元素,一边更新累积器,最后在处理完整个列表之后,返回累积器的值。
有两种不同类型的折叠,其中 foldl 从左边开始进行折叠,而 foldr 从右边开始进行折叠。