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

String 将字符串解析为二进制元组列表,string,parsing,haskell,tuples,lambda-calculus,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)] 不幸的是,我得到了类型不匹配,有人能给出任何提示如何解决这个问题吗 我的方向正确吗?有几个问题 map(\[a,b]>…)x中的模式[a,b]仅匹配两个元素的列表,因此编译器推断函数\[a,b]>…对于某些r和

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

我试图使用一个
map
像这样的
map(\[a,b]->(a::Char,b::Int))x
,其中
x
是字符串

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

不幸的是,我得到了类型不匹配,有人能给出任何提示如何解决这个问题吗


我的方向正确吗?

有几个问题

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

    编译器知道
    map
    具有类型
    (u->v)->[u]->[v]
    ,因此它将
    u
    [r]
    v
    s
    相结合,以推断
    map(\[a,b]->)的类型
    [[r]]->[s]

    这意味着
    x
    必须具有类型
    [[r]]
    ,也就是说,它必须是列表列表。但是您希望
    x
    成为
    字符串,它是
    [Char]
    的同义词。编译器无法统一
    [[r]]
    [Char]
    ,因此它会创建对象

  • 您正试图将
    a
    转换为
    Char
    并将
    b
    转换为
    Int
    ,就像在C中一样,但在Haskell中不能这样做。如果要将
    字符
    '1'
    转换为
    Int
    1
    ,则需要另一种方法,如
    读取
    ,可以使用该方法将
    字符串
    转换为
    Int

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

    首先考虑几个案例:

    • myParser”“
      返回什么
    • myParser“a1”
      返回什么
    • myParser[a,b]
      返回什么
    • myParser(a:b:cs)
      返回什么

      • 有几个问题

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

        编译器知道
        map
        具有类型
        (u->v)->[u]->[v]
        ,因此它将
        u
        [r]
        v
        s
        相结合,以推断
        map(\[a,b]->)的类型
        [[r]]->[s]

        这意味着
        x
        必须具有类型
        [[r]]
        ,也就是说,它必须是列表列表。但是您希望
        x
        成为
        字符串,它是
        [Char]
        的同义词。编译器无法统一
        [[r]]
        [Char]
        ,因此它会创建对象

      • 您正试图将
        a
        转换为
        Char
        并将
        b
        转换为
        Int
        ,就像在C中一样,但在Haskell中不能这样做。如果要将
        字符
        '1'
        转换为
        Int
        1
        ,则需要另一种方法,如
        读取
        ,可以使用该方法将
        字符串
        转换为
        Int

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

        首先考虑几个案例:

        • myParser”“
          返回什么
        • myParser“a1”
          返回什么
        • myParser[a,b]
          返回什么
        • myParser(a:b:cs)
          返回什么

        我想出了这个方法,但它确实不安全,因为它不会处理不正确的字符串,如
        “AA11B2C3”


        应该真正检查
        (a:b:rest)
        中的
        a
        b
        是否确实是
        Char
        Int
        。此外,它使用递归,您提到使用
        map
        可能还不够,而且它的类型非常多态。

        我提出了这个方法,但它确实不安全,因为它不会处理不正确的字符串,如
        “AA11B2C3”


        应该真正检查
        (a:b:rest)
        中的
        a
        b
        是否确实是
        Char
        Int
        。此外,它还使用递归,您提到使用
        map
        可能不够,而且它的类型非常多态。

        好吧,
        map
        实际上是用于将单个函数逐个应用于某个元素。如何拆分字符串需要上下文(知道下一个字母),因此
        map
        不是最佳选择

        但是,您说过您的解决方案必须符合
        map
        。这是可以做到的,但有点迂回。我想不出任何方法使
        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
        

        嗯,
        map
        实际上是指将单个函数逐个应用于某个元素。如何拆分字符串需要上下文(知道下一个字母),因此
        map
        不是最佳选择

        但是,您说过您的解决方案必须符合
        map
        。这是可以做到的,但有点迂回。我想不出任何方法使
        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
        

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

        这使我们认识到,一旦你有了“A1”、“B2”的列表,。。。这个
        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
        
        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