【问题标题】:Haskell Morse Code Exercise - how to add "gaps" between list itemsHaskell 摩尔斯电码练习 - 如何在列表项之间添加“间隙”
【发布时间】:2017-10-09 20:22:31
【问题描述】:

我目前正在使用 Haskell 开发莫尔斯电码编码器。这是我第一次在 Haskell 工作,我发现很难以“功能性”的方式思考。到目前为止,我想出了这个:

module Formative1
    (encode, decode, toTree, toTable)
where

import safe Lib

encode :: String -> [MorseUnit]
encode str = codeText(words str)

codeWord :: String -> [MorseUnit]
codeWord word = map codeSymbol word

codeText :: [String] -> [MorseUnit]
codeText list = map codeWord list

解释我的代码: encode 是主要功能。它使用words 将句子分解为单词列表。 codeText 用于将函数 codeWord 应用于列表中的每个单词。 codeWord 然后maps 覆盖每个单词中的每个字符,并使用codeSymbol 找到MorseUnit

我对这个解决方案有两个问题:

问题 1:由于[MorseUnit][[MorseUnit]] 不匹配,每次运行时都会出错,我相信这是因为我使用了两次map,所以它在列表中列出。但是如果不使用map,我想不出另一种解决方案。

Issue2:摩尔斯电码规则规定,每个字母后面应该有一个“shortGap”,每个单词后面应该有一个“mediumGap”。我不知道如何在列表的每个元素之间插入任一字符串(我会在 map codeSymbol word 的每个元素之间插入 shortGap,所以 shortGap 在每个字母之间,我会在 map codeWord list 的每个元素之间插入 mediumGap,所以 mediumGaps在每个单词之间)。我也不明白我会在什么时候添加空白,因为我不想不小心将codeSymbol 应用于任何空白并替换它们。

任何解决方案都会有所帮助!谢谢。

【问题讨论】:

  • 对于插入间隙,您可能需要intersperse。对于类型错误,您可能只想使用concat 将列表展平到位。在没有看到更多数据类型的情况下,这是我能提供的最好的帮助!
  • @jkeuhlen 感谢您的回复。通过使用 concat,我已经能够摆脱 [[MorseUnit]] 问题。但是,无论我在哪里插入“intersperse”,都会遇到匹配类型和范围的问题。这是我的代码: encode :: String -> [MorseUnit] encode str = codeText (words str) codeWord :: String -> [MorseUnit] codeWord word = intersperse("shortGap" (concat(map codeSymbol word))) codeText :: [String] -> [MorseUnit] codeText list = intersperse("mediumGap" (concat(map codeWord list)))

标签: list haskell types syntax morse-code


【解决方案1】:

问题 1

您完全正确,错误是由于两张地图造成的。但是您也可以从类型签名中看到它:

codeText list = map codeWord list (where list :: [String])
codeWord :: String -> [MorseUnit]
map :: (a -> b) -> [a] -> [b]

如果将(a -> b) 替换为(String -> [MorseUnit]),则[a] 变为[String][b] 变为[[MorseUnit]]

就像在数学中一样,有很多方法可以做到这一点。显然你可以concat 结果列表。注意简单的[[a]] -> [a] 签名。

请注意,紧随其后的是concatMap。您可以使用它来代替 map,它会为您平展结果。

为了进一步混淆你,如果你发现你的参数出现在声明和定义的最后,你可以省略它:

codeText = concatMap codeWord

我推荐你使用Hoogle来搜索你想要的函数签名。

问题 2

首先,由于您需要区分单词和符号,因此在我看来,您可能不需要这么早将列表展平。我不熟悉摩尔斯电码,所以我不完全理解你想在那里做什么。如果你可以用类型和签名来表达你的概念,那就更清楚了。我的建议是查看intersperse :: a -> [a] -> [a]intercalate :: [a] -> [[a]] -> [a] 以插入您的空白。它们需要使用import Data.List 导入。

【讨论】:

  • 感谢您的回复。我已经能够通过使用 concat 摆脱 [[MorseUnit]] 问题(稍后我将研究函数调用,但我的截止日期意味着我必须使用最快的解决方案)。但是,无论我在哪里插入“intersperse”,都会遇到匹配类型和范围的问题。如果我这样做,这是我的代码: encode :: String -> [MorseUnit] encode str = codeText (words str) codeWord :: String -> [MorseUnit] codeWord word = intersperse("shortGap" (concat(map codeSymbol word))) codeText :: [String] -> [MorseUnit] codeText list = intersperse("mediumGap" (concat(map codeWord list)))
  • codeWord 中可能不需要concatString 在 Haskell 中等于 [Char],所以你需要在 String 上使用 intercalate
  • 当我使用“插入”时,出现“变量不在范围内”错误
  • 对不起,我没有检查。添加import Data.List 导入。
  • 成功了,谢谢!剩下的唯一问题是在哪里放置“穿插”。无论我是在 concat 之前还是之后执行此操作,我都会收到有关匹配类型的错误。在这个例子中,我在 concat 之后完成了它:codeWord :: String -> [MorseUnit] codeWord word = intersperse("shortGap" (intercalate(map codeSymbol word))) codeText :: [String] -> [MorseUnit] codeText list = intersperse("mediumGap" (concat(map codeWord list)))
【解决方案2】:

碰巧,你需要一个 Monad。有很多(我的意思是很多)Haskell 的 Monad 教程可以在线获得,我鼓励你利用其中之一。

列表类型[]Monad 类型类的一个实例。不赘述,这意味着有一个函数concatMap :: (a -> [b]) -> [a] -> [b]。从类型中,您可以推断它类似于map :: (a -> b) -> [a] -> [b],但以某种方式扁平化(或concats)列表结构。

concatMap(>>=) :: Monad m => (a -> m b) -> m a -> m b,专门用于m ~ []

我们也可以省略函数的参数,因为 Haskell 中的函数是柯里化的。

encode :: String -> [MorseUnit]
encode = codeText . words

codeWord :: String -> [MorseUnit]
codeWord = map codeSymbol

codeText :: [String] -> [MorseUnit]
codeText = concatMap codeWord

至于第二个问题,请尝试使用intercalate。举个例子:

> intercalate ", " ["hello", "world"]
"hello, world"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-23
    相关资源
    最近更新 更多