首页 > 解决方案 > 在 Haskell 中理解 Vigenere 密码

问题描述

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

特别是递归函数调用和“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

标签: haskellvigenere

解决方案


这个想法是第一个列表中的每个字符都“移动”了由第二个列表中的相应字符确定的距离。但是,第二个列表的长度可能与第一个不同。如果更长,则没有问题;你会忽略额外的(如基本情况所见vig [] k = [])。但是,如果它更短,您将希望从头开始。

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

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

encrypt是什么实际加密;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 = ...

推荐阅读