List 使用什么代替列表理解

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

我刚开始学习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
或:

或在:

函数
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