我正在尝试在Haskell中将字符串"A1B2C3D4"
解析为[('A',1),('B',2),('C',3)]
。
我正在尝试使用map
这样的map (\[a, b] -> (a :: Char, b :: Int)) x
,其中x
是字符串。
这是我需要遵循的:: String -> [(Char, Int)]
函数签名。
不幸的是,我遇到类型不匹配的情况,任何人都可以提供任何提示以解决此问题吗?
我的方向正确吗?
答案 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)
有一些问题。
[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]
,因此它是对象。
您正尝试像在C语言中一样将[[r]]
投射到[Char]
并将a
投射到Char
,但是您不能在Haskell中做到这一点。如果要将b
之类的Int
转换为Char
'1'
,则需要另一种方法,例如Int
,可以使用该方法从从1
到read
。
这里有一些建议。不要使用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)
是否确实是Char
和Int
。另外,它使用递归,而您提到使用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