了解 Haskell 中的维吉尼亚密码

Understanding Vigenere cipher in Haskell

我无法理解下面代码中到底发生了什么。

特别是递归函数调用和 "ks++[k]" 以及 chr $65。我假设前者用于递归遍历列表,但如果有人能像我 5 岁那样解释我将不胜感激

vig (p:ps) (k:ks) = (encrypt p k):(vig ps (ks++[k])) 
  where
    encrypt b = chr $ 65 + mod (ord a + ord b) 26

完整代码

vig :: [Char] -> [Char] -> [Char]
vig [] k = []
vig p [] = []
vig (p:ps) (k:ks) = (encrypt p k):(vig ps (ks++[k]))
  where
    encrypt a b = chr $ 65 + mod (ord a + ord b) 26

想法是第一个列表中的每个字符 "shifted" 距离由第二个列表中的相应字符确定。但是,第二个列表的长度可能与第一个不同。如果更长,没有问题;您将忽略额外的部分(如基本情况 vig [] k = [] 所示)。但是,如果更短,您将希望从头开始。

假设您从调用 vig "horse" "dog" 开始。然后你将按顺序进行以下递归调用:

vig "orse" "ogd"
vig "rse" "gdo"
vig "se" "dog"
vig "e" "ogd"
vig "" "gdo"

encrypt 是实际加密的内容;它 returns 基于消息中的字符 p 和加密 k 中的字符 k 的新字符。每个消息字符只使用一次;每个键字符可以多次使用(如果键比消息短)或根本不使用(如果键长)。

一个更简单(也更有效)的方法是使用 cycle 函数从键创建一个无限的重复列表。 (如果 ks 已经足够长,它只会让你最终忽略一个更长的字符列表。因为 Haskell 是惰性的,所以这种简化抽象没有成本。)

vig ps ks = vig' ps (cycle ks)
  where vig' _ [] = p
        vig' [] _ = []
        vig' (p:ps) (k:ks) = encrypt p k : vig' ps ks
        encrypt a b = ...

然而,vig' 并不真正关心任一列表的内容或 encrypt 的作用。它有点像 map,除了不是通过对每个元素应用一个函数从另一个列表构建一个列表,而是通过对每个相应的元素应用一个函数从 两个 列表构建一个列表对元素。此模式预定义为 zipWith 函数。

vig ps ks = zipWith encrypt ps ks  -- vig = zipWith encrypt
  where encrypt a b = ...