我刚刚开始使用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)]
答案 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']
import Data.List
import Data.Maybe
let2num = fromJust . (flip elemIndex) ['a'..'z']
函数elemIndex
返回给定列表中第一个元素的索引(等于==
)到查询元素,如果没有这样的元素,则返回Nothing
。
Maybe
类型封装了一个可选值。类型Maybe a
的值包含类型a
的值(表示为Just a
),或者为空(表示为Nothing
)。使用Maybe
是处理错误或异常情况的好方法,而不会采取错误等严厉措施。
函数fromJust
从Just
中提取元素。
答案 1 :(得分:4)
相反的过程:
num2let = (!!) ['a'..'z']
!!
是List index (subscript) operator,从0
开始。它是更通用的Data.List.genericIndex
的一个实例,它采用任何整数类型的索引。
(!!)
是partially applied,这意味着它仍然需要一个类型Int
的参数来产生结果(列表中的值等于Int
值的值你转到num2let
)。
答案 2 :(得分:3)
“Caesar简单地将字母中的每个字母替换为字母表中的三个字母,在字母表的末尾包裹着。”我们可以简单地在Haskell中编写它。实际上,我们可以完全避免let2num
和num2let
。
因此,我们首先定义一个表,将纯文本字母映射到密文字母:
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
类的一种方法,它允许在Int
和Char
之间进行转换,方式与{{{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
所需的偏移量。