使用什么而不是列表理解

时间:2011-01-26 08:59:09

标签: list haskell functional-programming list-comprehension

我刚刚开始使用Haskell并完成了一个很好的exercise来制作一个Caesar密码。

最初的步骤之一是创建一个函数,它将接收一个字母并将其转换为数字。我知道chr和ord已经可以做到这一点但是练习的一部分是写自己的。

let2num c = head [ b | (a,b) <- zip ['a'..'z'] [0..25], a==c]

我是Haskell语法的新手,我学到的第一件事就是列表理解,所以这已成为我的锤子。我很好奇,写这个函数的另一种(可能更好)方法是什么?

如果你很好奇,密码的其余部分位于gist

修改

我也对从数字转换成字母的其他方式感兴趣。

num2let d = head [ a | (a,b) <- zip ['a'..'z'] [0..25], b==(d `mod` 26)]

6 个答案:

答案 0 :(得分:4)

我的解决方案:

import Data.List
let2num c = let (Just n) = elemIndex c ['a'..'z'] in n

或者:

import Data.List
import Data.Maybe
let2num c = fromJust $ elemIndex c ['a'..'z']

pointless style

import Data.List
import Data.Maybe
let2num = fromJust . (flip elemIndex) ['a'..'z']

函数elemIndex返回给定列表中第一个元素的索引(等于==)到查询元素,如果没有这样的元素,则返回Nothing

Maybe类型封装了一个可选值。类型Maybe a的值包含类型a的值(表示为Just a),或者为空(表示为Nothing)。使用Maybe是处理错误或异常情况的好方法,而不会采取错误等严厉措施。

函数fromJustJust中提取元素。

答案 1 :(得分:4)

相反的过程:

num2let = (!!) ['a'..'z']

!!List index (subscript) operator,从0开始。它是更通用的Data.List.genericIndex的一个实例,它采用任何整数类型的索引。

这里

(!!)partially applied,这意味着它仍然需要一个类型Int的参数来产生结果(列表中的值等于Int值的值你转到num2let)。

答案 2 :(得分:3)

“Caesar简单地将字母中的每个字母替换为字母表中的三个字母,在字母表的末尾包裹着。”我们可以简单地在Haskell中编写它。实际上,我们可以完全避免let2numnum2let

因此,我们首先定义一个表,将纯文本字母映射到密文字母:

cipher = let abc = ['a'..'z']
             code = drop 3 abc ++ take 3 abc
         in  zip abc code

看起来像

[('a','d'),('b','e'),('c','f'),('d','g'), ... ]

现在我们可以加密一个符号,如果我们只是lookup这个字典中的字母:

ghci> lookup 'a' cipher
Just 'd'

lookup返回Maybe Char值,我们需要将其转换为Char,为此,我使用maybe函数,使用'?'在密码中找不到的符号,id(识别函数=无变化)到找到的符号:

ghci> maybe '?' id (lookup 'a' cipher)
'd'

现在我们可以编写一个encrypt函数来编码一个符号,它会留下缺失的字符,就像空格一样,未加密:

encrypt c = maybe c id (lookup c cipher)

加密整个字符串:

ghci> map encrypt "haskell is fun"
"kdvnhoo lv ixq"

所以我们可以把它们放在一起:

encrypt c = maybe c id (lookup c cipher)
  where
  cipher = let abc = ['a'..'z']
               code = drop 3 abc ++ take 3 abc
           in  zip abc code

答案 3 :(得分:2)

为了完整起见,我认为有人应该提到列表推导只是在列表monad中编写内容的捷径。您转录的代码大致是:

let2num c = head $ do (a,b) <- zip ['a'..'z'] [0..25]
                      if a == c then [b] else []

不是一个非常有趣的例子,但你去了。

另外,对do语法进行去糖,这是相同的:

let2num c = head $ zip ['a'..'z'] [0..25] >>= \(a,b) -> if a == c then [b] else []

答案 4 :(得分:0)

我不确定你为什么反对ord解决方案。基于列表的解决方案执行不必要的工作(遍历列表)。而且他们仍然被调查为enumFromTo的调用,这是Enum类的一种方法,它允许在IntChar之间进行转换,方式与{{{1}相同。 1}} / ord做。这是为chr类型提供的最低级别界面,所以你几乎不能在这里“自己编写”(除了自己做拳击/拆箱,但这不是一个很大的乐趣)。

答案 5 :(得分:0)

我会选择以下内容:

import Data.Char

caesar :: Int -> Char -> Char
caesar n c = if isAlpha c 
             then chr (ord 'a' + (ord c - ord 'a' + n) `mod` 26) 
             else c

map (caesar n)在字符串上n所需的偏移量。