取回原始x:xs

时间:2015-04-08 19:39:08

标签: list haskell return

我想在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

3 个答案:

答案 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并返回其值。这只会返回最后一个结果。使用:运算符前置中间编码值以获取完整编码的字符串。