【问题标题】:Pattern matching from a [String] in Haskell来自 Haskell 中的 [String] 的模式匹配
【发布时间】:2012-09-17 20:53:12
【问题描述】:

我正在学习关于函数式编程的入门课程,我们使用 Haskell。 练习的一部分是为输入字符串编写解析器。

但是我无法解决以下错误,或了解实际发生的情况。

Parser.hs:29:71:
Couldn't match expected type `String' with actual type `Char'
In the first argument of `readPoint', namely `start'
In the expression: readPoint start
In the expression:
  (readLines track, readPoint start, readLine finish)

错误源于此行:

readTrack str = parseTrack (lines str) where
    parseTrack (start : finish : track) = (readLines track, readPoint start, readLine finish)

我预期会发生的是输入字符串被拆分为行列表,然后传递给 parseTrack。 然后 parseTrack 将使用模式匹配来命名列表中的前两个字符串(行)以及其余的。

但是我相信发生的是,finish 是列表中的顶部元素,并且 start 被分配了该字符串中的顶部字符。

我真的很想知道如何解决这个问题以及实际发生了什么。

非常感谢!

解析器.hs

module Parser where

import Types

readFloat :: String -> Float
readFloat str = case reads str of
    [] -> error "not a floating point number"
    (p,_):_ -> p

readInt :: String -> Int
readInt str = case reads str of
    [] -> error "not an integer"
    (p,_):_ -> p

readPoint :: String -> Point
readPoint str = parsePoint (words str) where
    parsePoint (x : y : _) = (readInt x, readInt y)

readLine :: String -> Line
readLine str = parseLine (words str) where
    parseLine (x1 : y1 : x2 : y2 : _) = ((readInt x1, readInt y1), (readInt x2, readInt y2))

readLines :: String -> [Line]
readLines str = parseLines (lines str) where
    parseLines (line : rest) = readLine line : parseLines rest

readTrack :: String -> Track
readTrack str = parseTrack (lines str) where
    parseTrack (start : finish : track) = (readLines track, readPoint start, readLine finish)

Types.hs

module Types where

type Vector2D   = (Int, Int)
type Point      =  Vector2D
type Line       = (Point, Point)
type Velocity   =  Vector2D

type CarState   = (Position, Velocity)
type Position   =  Vector2D
type Trace      = [Position]

type Track      = ([Line], Point, Line)

【问题讨论】:

    标签: list haskell


    【解决方案1】:

    您的变量track 实际上是一个单行列表,而不是其中包含'\n's 的字符串。由于您已经将其拆分为 lines,因此您只需将其 map readLine 覆盖,给出:

    readTrack str = parseTrack (lines str) where
        parseTrack (start:finish:tracks) 
                         = (map readLine tracks, readPoint start, readLine finish)
    

    这里是tracks :: [String],这就是为什么你可以map readLine 全部使用它们 - 你不需要先使用readLines 将它分成几行。 (你可以说它是一个列表,因为它是 : 右侧的最后一件事。)

    你说

    但是我相信发生的是,finish 是列表中的顶部元素,并且 start 被分配了该字符串中的顶部字符。

    发生的事情是:因为你要求readLines track 作为第一个输出,Haskell 从那里开始,因为你声明了

    readLines :: String -> [Line]
    

    这意味着 track 必须是一个字符串 - 这是 readLines 唯一可以处理的事情。

    首先,你需要记住:左边有一个元素,右边有一个列表,所以在

    3:4:stuff
    

    stuff 必须是 [Integer],因为它位于某些 Integer 元素的右侧。同样,

    c:"a string"
    

    表示 c 必须是 Char,因为 String = [Char]。

    在您的代码中,我们发现track 是一个字符串,这意味着当您编写时

    (start : finish : track)
    

    开始和结束都必须是可以放在字符串前面的元素,因此开始和结束都必须是字符。

    Haskell 然后查看您的代码 readPoint start,但是因为它已经确定 start 具有 Char 类型,但是

    readPoint :: String -> Point
    

    它抱怨 Char 和 String 不匹配。

    我认为你犯了错误是因为你忘记了 readLines 接受一个字符串,但它感觉(从名字上看)它应该很高兴地接受一个字符串列表。您的 parseLines 看起来像做类似的事情,但它需要一个字符串列表,所以可以处理,而 readLines 需要一个带有换行符的字符串,所以不能处理列表。

    【讨论】:

    • 很好的答案,解决了这个问题,并提高了我对如何评估类型的理解。 +1 先生!
    【解决方案2】:

    UPD。哦,抱歉,我没听懂 track 表示多个轨道,并且必须是 [String] 类型。所以 AndrewC 的回答更合适。

    因为在 haskell 中,模式(x:xs) 意味着如果x 具有类型a,那么xs 必须是[a] 类型,您在parseTrack 中的模式意味着在类型smth 中,例如(a : a : [a])
    编译器想要评估类型a,首先它在右边看到的是readLines track。 Func readLines 的类型为 String -> [Line],因此编译器的 trackString,这意味着 [a] 的类型为 String。同样在haskell String[Char] 所以aChar
    但是你需要a 作为String。所以你只需要取前三个字符串并扔掉[String] 的其余部分。在类型中,它意味着像(String : String : String : [String]) 这样的东西。为此,您可以将parseTrack 中的匹配模式重写为:

    parseTrack (start : finish : track : _)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-09
      • 2016-04-26
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多