Haskell 中的凯撒移位函数?

Caesar Shift function in Haskell?

我必须编写一个移位函数来从 "table"(列表)中搜索元素对。如果该元素不在给定列表中,则它必须给出一个 '#' az 结果。

示例:

shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'a' == 'f'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'b' == 'g'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'b' == 'g'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'x' == '#'

我的代码:

shift :: [(Char,Char)] -> Char -> Char
shift z c = [b |(a,b)<-z,a==c]!!0

它有效,但不适用于例外情况。我似乎无法让它适用于不在列表中的元素。 我试过:

shift z c 
    | c `elem` z =[b |(a,b)<-z,a==c]!!0
    | otherwise ='#'

和辅助函数:

isgood z c
    | c `elem` z = (shift z c)
    | otherwise = '#'

但它们不起作用。如何解决?

您可以通过多种方式来编写本文,它们基本上都是做同样的事情。

一种方法只是对您之前的一种尝试稍作修改,"works, but not for the exceptions":

shift :: [(Char,Char)] -> Char -> Char
shift z c = [b |(a,b)<-z,a==c]!!0

我相信您已经观察到,唯一的问题是当您找不到字符时程序崩溃,而不是函数根据需要为您提供“#”。这是因为 (!! 0)head(它等效)一样,在应用于空列表时会失败并出现难看的运行时错误。所以你需要做的就是检查列表是否为空,然后取第一个元素或给出默认答案。在我看来,最巧妙的方法是在 case 表达式中进行模式匹配:

shift :: [(Char,Char)] -> Char -> Char
shift z c = case [b |(a,b)<-z,a==c] of
    [] -> '#'
    (c:_) -> c

但是这种以成对列表的形式在 "lookup table" 中查找内容的概念是如此标准,以至于 Haskell Prelude 已经有一个功能,自然地称为 lookup。它 returns 一个 Maybe 类型作为处理失败的安全方式,如果你愿意,你可以用与以前版本类似的方式对结果进行模式匹配:

shift z c = case (lookup c z) of
    Nothing -> '#'
    Just c -> c

这可以使用另一个标准库函数 fromMaybe 进一步简化:

shift z c = fromMaybe '#' (lookup c z)

还有两点要完成:

  • 使用成对列表作为类似数据结构的字典,虽然对于简单的情况没问题,但效率不高。最好为此使用 Map 等专用结构。
  • shift 是这个函数的奇怪名称,因为它需要查找 table 任意对。对于真正的凯撒移位密码,table 隐含在移位数中,您需要一个类型为 Int -> Char -> Char 的函数,其实现方式会大不相同。