Haskell中的凯撒移位函数?

问题描述 投票:0回答:1

我必须写一个移位函数,从一个 "表"(列表)中搜索一个元素的对。如果元素不在给定的列表中,它必须给出一个'#'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 = '#'

但都没有用 如何解决这个问题?

list function haskell shift caesar-cipher
1个回答
3
投票

你可以有很多方法来写这个,基本上都是做同样的事情。

其中一种方法只是对你之前的一个尝试稍加修改,也就是 "可行,但例外情况下不行 "的方法。

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

但这种在 "查找表 "中以对子列表的形式查找东西的概念是如此标准,以至于Haskell Prelude已经有了一个函数,自然叫做: 查找. 它返回一个 Maybe 类型作为处理失败的安全方式,如果你想的话,你可以用类似于上一版本的方式对结果进行模式化匹配。

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

如果你愿意的话,你可以用类似于上一个版本的方法来进行模式匹配: 从可能,另一个标准库函数。

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

还有两点需要补充:

  • 使用对的列表作为一个类似字典的数据结构,虽然对于简单的情况来说是可以的,但是效率并不高。最好使用专门的结构,如 地图 对于这个函数来说。
  • shift 这个函数的名字很奇怪,因为它需要一个任意对的查找表。对于真正的凯撒移位密码来说,这个表是隐含在移位数中的,你需要一个类型为 Int -> Char -> Char这将是相当不同的执行方式。
© www.soinside.com 2019 - 2024. All rights reserved.