使用什么来代替列表理解

发布于 2024-10-14 18:31:16 字数 608 浏览 2 评论 0原文

我刚刚开始使用 Haskell 并完成了一个很好的练习< /a> 制作凯撒密码。

第一步是创建一个函数,将字母转换为数字。我知道 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)]

I'm just getting started with Haskell and finished a nice exercise to make a Caesar cipher.

One of the first steps was to make a function that will take a letter and turn it into a number. I know that chr and ord can do this already but part of the exercise was to write your own.

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

I'm new to the Haskell syntax and one of the first things I learned was list comprehensions, so that has become my hammer. I'm very curious though, what is another (likely better) way to write this function?

If you're curious the rest of the cipher is in a gist.

EDIT

I'm also interested in other ways to translate back from numbers to letters.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦境 2024-10-21 18:31:16

我的解决方案:

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),要么为空(表示为 >什么都没有)。使用 Maybe 是处理错误或异常情况的好方法,而无需采取错误等严厉措施。

函数 fromJustJust 中提取元素。

My solution:

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

Or:

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

Or in pointless style:

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

The function elemIndex returns the index of the first element in the given list which is equal (by ==) to the query element, or Nothing if there is no such element.

The Maybe type encapsulates an optional value. A value of type Maybe a either contains a value of type a (represented as Just a), or it is empty (represented as Nothing). Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as error.

The function fromJust extracts the element out of a Just.

小姐丶请自重 2024-10-21 18:31:16

相反的过程:

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

!! 是一个 列表索引(下标)运算符,从0开始。它是更通用的 Data.List.genericIndex 的实例,它采用任何整数类型的索引。

(!!) 在这里部分应用,这意味着它仍然需要一个 Int 类型的参数来生成结果(列表中的一个值,其索引等于您传递给 num2letInt 值)。

The reverse process:

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

!! is a List index (subscript) operator, starting from 0. It is an instance of the more general Data.List.genericIndex, which takes an index of any integral type.

(!!) is partially applied here, which means it still needs one argument of type Int to yield the result (a value from the list whose index equals to Int value you pass to num2let).

泼猴你往哪里跑 2024-10-21 18:31:16

“凯撒只是将消息中的每个字母替换为字母表中向下三个位置的字母,环绕在字母表的末尾。”我们可以简单地用 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'), ... ]

现在我们可以加密一个符号,如果我们简单地在这个字典中查找字母:

ghci> lookup 'a' cipher
Just 'd'

lookup 返回一个 Maybe Char 值,我们需要将其转换为简单的 Char,为此我使用 maybe 函数,使用'?' 表示在密码中未找到的符号,id(身份函数=无更改)表示找到的符号:

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

现在我们可以编写一个 加密< /code> 函数只对一个符号进行编码,它会留下缺失的字符,例如未加密的空格:

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

“Caesar simply replaced each letter in the message by the letter three places further down the alphabet, wrapping around at the end of the alphabet.” We can simply write it in Haskell. In fact we can avoid let2num and num2let altogether.

So let's start with defining a table to map plain text alphabet to the cipher text alphabet:

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

It will look like

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

Now we can encrypt a symbol, if we simply lookup the letter in this dictionary:

ghci> lookup 'a' cipher
Just 'd'

lookup returns a Maybe Char value, we need to convert it to simply a Char, and for this I use maybe function, using '?' for symbols which were not found in the cipher, and id (identity function = no changes) to found symbols:

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

Now we can write an encrypt function to encode just one symbol, it will leave missing characters, like a space, unencrypted:

encrypt c = maybe c id (lookup c cipher)

To encrypt an entire string:

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

So we can put it all together:

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
等待圉鍢 2024-10-21 18:31:16

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

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 []

For completeness, I think somebody should mention that list comprehensions are just a shortcut for writing stuff in the list monad. Your code transcribed is, roughly, this:

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

Not a very interesting example, but there you go.

Also, de-sugaring the do syntax, this is the same:

let2num c = head $ zip ['a'..'z'] [0..25] >>= \(a,b) -> if a == c then [b] else []
゛清羽墨安 2024-10-21 18:31:16

我不确定您为什么反对 ord 解决方案。基于列表的解决方案执行不必要的工作(遍历列表)。它们仍然被脱糖为调用 enumFromTo,这是 Enum 类的一个方法,它允许在 IntChar 之间进行转换ord/chr的方式相同。这是为 Char 类型提供的最低级接口,因此您几乎无法在这里“编写自己的”(除了自己进行装箱/拆箱,但这并不是一个很大的乐趣)。

I'm not sure why you are opposed to the ord solution. List-based solutions perform unnecessary work (traversing a list). And they still are desugared into invocation of the enumFromTo, a method of the Enum class which allows to convert between Ints and Chars in the same way as ord/chr do. This is the lowest-level interface provided for Char type, so you hardly can "write your own" here (apart from doing boxing/unboxing yourself, but this is not a big joy).

情话墙 2024-10-21 18:31:16

我会使用以下内容:

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

并使用 n 所需的偏移量在字符串上进行 map (caesar n)

I would go with the following:

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

and map (caesar n) over the string with n the desired offset.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文