将字符串解析为二进制元组列表

时间:2018-11-07 22:22:15

标签: string parsing haskell tuples lambda-calculus

我正在尝试在Haskell中将字符串"A1B2C3D4"解析为[('A',1),('B',2),('C',3)]

我正在尝试使用map这样的map (\[a, b] -> (a :: Char, b :: Int)) x,其中x是字符串。

这是我需要遵循的:: String -> [(Char, Int)]函数签名。

不幸的是,我遇到类型不匹配的情况,任何人都可以提供任何提示以解决此问题吗?

我的方向正确吗?

4 个答案:

答案 0 :(得分:2)

好吧,map的真正含义是将单个函数一个接一个地应用于每个元素。根据需要拆分字符串需要上下文(知道下一个字母),因此map不是这里的最佳选择。

但是,您说您的解决方案必须是map。可以做到,但是有点round回。我想不出任何方法来map拆分实际的字符串,但是可以肯定地将其转换为正确的类型:

isDigit :: Char -> Bool
isDigit c = elem c ['0'..'9']

split :: String -> [(Char, Int)]
split str = let chars  = filter (not . isDigit) str
                nums   = filter isDigit str
                zipped = zip chars nums in
              map (\(a, b) -> (a, read [b])) zipped

答案 1 :(得分:1)

有一些问题。

  1. [a, b]中的模式map (\[a, b] -> ...) x仅匹配两个元素的列表,因此编译器推断函数\[a, b] -> ...的类型[r] -> s对于某些{{1 }}和r

    编译器知道s的类型为map,因此它将(u -> v) -> [u] -> [v]u[r]v统一来推断为s输入[[r]] -> [s]

    这意味着map (\[a, b] -> ...)必须具有类型x,即它必须是列表列表。但是您希望[[r]]x的{​​{1}}的同义词。编译器无法统一String[Char],因此它是对象。

  2. 您正尝试像在C语言中一样将[[r]]投射到[Char]并将a投射到Char,但是您不能在Haskell中做到这一点。如果要将b之类的Int转换为Char '1',则需要另一种方法,例如Int,可以使用该方法从从1read

这里有一些建议。不要使用String。尝试编写一个递归解决方案。

首先考虑几种情况:

  • Int返回什么?
  • map返回什么?
  • myParser ""返回什么?
  • myParser "a1"返回什么?

答案 2 :(得分:1)

我想到了这一点,但它确实不安全,因为它不能处理像"AA11B2C3"这样的错误字符串!

splitingN :: Int -> [a] -> [[a]]
splitingN _ [] = []
splitingN n l
  | n > 0 = take n l : splitingN n (drop n l)
  | otherwise = error "uhhhhhh"

tuplify :: String -> (Char, Int)
tuplify a = (head a, read $ tail a)

stringy :: String -> [(Char, Int)]
stringy s = tuplify <$> splitingN 2 s

> stringy "A1B2C3D4" == [('A',1),('B',2),('C',3),('D',4)]

一种更好但又不完全安全的方法是:

stringy :: [a] -> [(a, a)]
stringy [] = []
stringy (a : b : rest) = (a, b) : splitting rest
stringy [a] = error "uhhhhh"

应该真正检查a中的b(a : b : rest)是否确实是CharInt。另外,它使用递归,而您提到使用map,所以可能不够用,并且它的类型相当多态。

答案 3 :(得分:0)

正如其他人指出的那样,您需要了解map将给定函数应用于列表的每个成员。了解了这一点之后,您将意识到,无法通过在现有列表上应用函数来获得所需的转换。

这将使您认识到,一旦有了“ A1”,“ B2”,...的列表,便可以将其转换为地图功能。

我在下面给出了功能代码。 split'函数是不安全的,因为它在很多情况下可能会崩溃(预期一个可以完美拆分为2个字符的字符串)。我还使用了功能digitToInt,为此您需要import Data.Char。您确实说过不想导入,在这种情况下,您可以编写自己的digitToInt函数,查看库代码,这非常简单。

import Data.Char

split' :: String -> [String]
split' [] = []
split' (x:y:xs) = (x:[y]) : split' xs

convert :: String -> [(Char, Int)]
convert input = map (\s -> (s!!0 ,  digitToInt(s!!1) )) $ split' input