我想在Haskell中创建一个函数,如下所示:
encode :: [(Char,Char)] - >字符串 - >串
我有一本"字典"函数叫做" table"这会创建一对配对列表:
table :: Int -> [Char] -> [(Char, Char)]
table __ [] = []
table n l = zip l (drop n (cycle l))
encode函数按字符对字符串进行编码。到目前为止它看起来像这样:
encode (x:xs) (n:ns)
| length(n:ns) > 1 && (fst x) == n = [snd x] ++ (encode (x:xs) ns)
| length(xs) > 1 && (fst x) /= n = encode xs (n:ns)
| length(ns) > 0 && length(xs) == 0 = encode (x:xs) ns
对于测试encode table2 "SOS"
,它只给我" U"但它必须是" UQU"。
table2是常量:table2 = table 2 (['a'..'z'] ++ ['A'..'Z'] ++ ['.', '!', '?', ' '])
我的问题是:如何找回(x:xs)的原始列表?我想我只能得到一个" U"因为我总是带着xs返回,它会切断第一对,但我需要每个角色的整个原始(x:xs)。
常量函数返回:picture
答案 0 :(得分:5)
您的功能问题是当您搜索关联列表时,您会在开头丢弃值。您只需要保留对该列表的引用。您可以执行类似
的转换encode l n0 = encode' l n0 where
encode' (x:xs) (n:ns)
| ... = snd x : encode (x:xs) ns0
但是你可以认识到更高级别的抽象会使这个问题变得更加简单。
假设您有一个可以编码一个Table
的抽象类型Char
。编码可能会失败(字符可能没有关联),因此它返回Maybe Char
:
type Table = ...
encode1 :: Table -> Char -> Maybe Char
然后要对整个字符串进行编码,只需使用相同的表对每个字符进行编码 - 如果有的话返回Nothing
,然后返回Nothing
。字符串是一个列表,您需要将具有效果的函数应用于该列表的每个元素 - 这正是mapM
所做的:
encode :: Table -> [Char] -> Maybe Char
encode t = mapM (encode1 t)
现在问题是如何定义Table
。由于它表示关联映射,为什么不使用关联映射数据类型 - Data.Map
。
import qualified Data.Map as M
type Table = M.Map Char Char
encode1 :: Table -> Char -> Maybe Char
encode1 = flip M.lookup
table :: Int -> [Char] -> [(Char, Char)]
table _ [] = []
table n l = zip l (drop n (cycle l))
table' :: Int -> [Char] -> Table
table' n l = M.fromList (table n l)
现在:
> encode (table 2 (['a'..'z'] ++ ['A'..'Z'] ++ ['.', '!', '?', ' '])) "SOS"
Just "UQU"
答案 1 :(得分:4)
我认为你的问题真的源于试图一次做太多。而不是使用您想要编码的整个字符串,一次只使用一个字符。如果您可以正确编码一个字符,那么您可以对它们进行全部编码:
encodeChar :: [(Char, Char)] -> Char -> Char
encodeChar table charToEncode = ???
要对单个字符进行编码,我们要在pair
table
中找到fst pair == c
。这可以使用基本递归和模式匹配来执行:
encodeChar [] _ = error "Empty table!"
encodeChar ((fromChar, toChar):restOfTable) charToEncode
= if fromChar == charToEncode
then toChar
else encodeChar restOfTable charToEncode
但是,使用error
是不好的做法。对于大多数情况,最好返回Maybe
。对我们来说幸运的是Haskell附带了一个名为lookup
的便捷功能,它可以为我们完成整个操作,但首先我会展示手动返回Maybe Char
的样子:
encodeCharMaybe [] _ = Nothing
encodeCharMaybe ((fromChar, toChar):restOfTable) charToEncode
= if fromChar == charToEncode
then Just toChar
else encodeCharMaybe restOfTable charToEncode
使用lookup
:
encodeCharMaybe table charToEncode = lookup charToEncode table
现在我们可以更简单地实现encode
:
encode1 :: [(Char, Char)] -> String -> String
encode1 table msg = map (encodeChar table) msg
此处table
每个encodeChar
调用使用相同的Maybe
,这会导致您当前的实施出现问题。如果您想对encode2 :: [(Char, Char)] -> String -> Maybe String
encode2 table [] = Just []
encode2 table (c:msg) = do
encodedC <- encodeCharMaybe table c
encodedMsg <- encode2 table msg
Just (encodedC : encodedMsg)
使用正确的错误处理,可以将其实现为
mapM
或使用encode2 table msg = mapM (encodeCharMaybe table) msg
,这基本上只是这种模式的概括
where
如上面的评论所述,您可以在encode2 table msg = mapM (encodeCharMaybe table) msg
where
encodeCharMaybe table charToEncode = lookup charToEncode table
子句中使用辅助函数,这看起来像
encode2 table msg = mapM (\charToEncode -> lookup charToEncode table) msg
虽然你可以在一个函数中写下所有这些:
encode2 = mapM . flip lookup
如果你想让人感到困惑,可以用pointfree style:
来写{{1}}
但这完全是可选的,当然不是最直观的解释。我只在这里展示它以展示Haskell有多棒!您的整个问题可以简化为几个内置函数和组合。
答案 2 :(得分:1)
首先,您可以使用模式匹配来避免检查列表的长度(x:xs)。理解你的功能发生了什么会更容易。
要回答您的问题,看起来您在第二行递归调用encode并返回其值。这只会返回最后一个结果。使用:
运算符前置中间编码值以获取完整编码的字符串。