List 使用什么代替列表理解
我刚开始学习Haskell,完成了一个很好的Caesar密码 第一步是制作一个函数,将字母转换成数字。我知道chr和ord已经可以这样做了,但部分练习是编写自己的List 使用什么代替列表理解,list,haskell,functional-programming,list-comprehension,List,Haskell,Functional Programming,List Comprehension,我刚开始学习Haskell,完成了一个很好的Caesar密码 第一步是制作一个函数,将字母转换成数字。我知道chr和ord已经可以这样做了,但部分练习是编写自己的 let2num c = head [ b | (a,b) <- zip ['a'..'z'] [0..25], a==c] let2num c=head[b |(a,b)我的解决方案: import Data.List let2num c = let (Just n) = elemIndex c ['a'..'z'] in n
let2num c = head [ b | (a,b) <- zip ['a'..'z'] [0..25], a==c]
let2num c=head[b |(a,b)我的解决方案:
import Data.List
let2num c = let (Just n) = elemIndex c ['a'..'z'] in n
或:
或在:
函数elemIndex
返回给定列表中与查询元素相等(按=
)的第一个元素的索引,如果没有这样的元素,则返回Nothing
Maybe
类型封装了一个可选值。类型的值可能A
包含一个A
类型的值(表示为仅为A
),也可能为空(表示为无
)。使用可能是一种处理错误或异常情况的好方法,而无需采取诸如错误之类的激烈措施
函数fromJust
从Just
中提取元素。“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'), ... ]
现在我们可以加密一个符号,如果我们只需查找本词典中的字母:
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
相反的过程:
num2let = (!!) ['a'..'z']
!!
是一个,从0
开始。它是更通用的Data.List.genericIndex
的一个实例,它采用任何整数类型的索引
(!!)
在这里,这意味着它仍然需要一个类型为Int
的参数来产生结果(列表中的一个值,其索引等于传递给num2let
的Int
值)。我不确定您为什么反对ord
解决方案。基于列表的解决方案执行不必要的工作(遍历列表)。它们仍然被分解为调用enumFromTo
,这是Enum
类的一种方法,它允许在Int
s和Char
s之间进行转换,转换方式与ord
/chr
do相同。这是为Char
类型提供的最低级别接口,因此您几乎无法使用它“写你自己的”在这里(除了自己装箱/拆箱,但这不是一个很大的乐趣)。为了完整起见,我想有人应该提到,列表理解只是在列表单子中写东西的捷径。你转录的代码大致如下:
let2num c = head $ do (a,b) <- zip ['a'..'z'] [0..25]
if a == c then [b] else []
我同意以下几点:
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(凯撒n)
在字符串上加上所需的偏移量n
。+1谢谢!我需要做一些谷歌搜索来弄清楚每件事的含义,但这是非常有用的。对反向过程有什么想法吗?当从一个数字返回到一个字母时,我认为elemIndex不会有帮助。我明白为什么他们将它命名为而不是>不公正
;)我读到你想写let2num
函数,并假设你想用它来实现替换密码。我想说明的是,该密码可以在不显式计算序数的情况下实现,但需要一个转换表。是的,最终目标是一个替换密码,但如果你阅读整个问题,你会注意到,实际的问题本质上是“没有chr和ord,从零开始编写num2let的更好方法是什么?”这太棒了!不幸的是,我只能接受你的一个答案:)严格地说,是的,就像原来的一样(见Roman的评论)。但是,如果您的意思是,这应该比原来的更有效,这并不明显,因为您仍在遍历一个链表。如果您希望将“0”转换为“25”的步骤数相同,则需要不同的数据结构(我将使用数组)。或者在节表示法中:num2let=(['a'..'z'])
我想知道头[b |(a,b)@Yasir:严格来说是O(1)因为只有26个小写字母。但是,它不是非常有效。请看我的答案。@罗马:但是它首先创建一个拉链列表,然后创建满足<代码> A==C < /COD>的元素的另一个列表,然后提取一个元素:相当多的工作要考虑O(1)。,嗯。:-)但我可能错了。@Yasir:渐进复杂性并不取决于任何人的主观判断。如果它需要有限的步数,那么它是O(1)。而且你的判断仍然不精确:懒惰的评估(特别是懒惰的构造/消费)让它执行不同于乍一看的操作。@Yasir:它们确实适用于Unicode,因为Char
只是一个Unicode码点。例如:ord'stu'
->1099
@Roman:呵呵,的确,完全忘记了数据。Char
是Unicode识别的。@Roman-我认为OP与ord
相反,因为这是他的家庭作业。如果你不能使用Ord
并且不想遍历列表,我认为最好的选择是一个不可变的数组。但这是一个人为的问题和解决方案。她说这是练习的一部分,所以让我们不必争论已经决定了什么,嗯?我一点也不反对Ord/chr解决方案,我只是在遵循印度国家科学院
num2let = (!!) ['a'..'z']
let2num c = head $ do (a,b) <- zip ['a'..'z'] [0..25]
if a == c then [b] else []
let2num c = head $ zip ['a'..'z'] [0..25] >>= \(a,b) -> if a == c then [b] else []
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