来自Haskell中[String]的模式匹配

Tim*_*jer 4 haskell list

我正在学习函数式编程的入门课程,我们使用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)
Run Code Online (Sandbox Code Playgroud)

错误源自此行:

readTrack str = parseTrack (lines str) where
    parseTrack (start : finish : track) = (readLines track, readPoint start, readLine finish)
Run Code Online (Sandbox Code Playgroud)

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

然而,我认为正在发生的是,finish是列表中的顶级元素,并且start从该字符串分配了顶部char.

我真的很想知道如何解决这个问题以及实际发生的事情.

非常感谢!

Parser.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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

And*_*ewC 8

您的变量track实际上是单行列表,而不是包含'\n's 的字符串.既然你已经把它分成了lines,你就可以把它分开map readLine,给出:

readTrack str = parseTrack (lines str) where
    parseTrack (start:finish:tracks) 
                     = (map readLine tracks, readPoint start, readLine finish)
Run Code Online (Sandbox Code Playgroud)

在这里tracks :: [String],这就是为什么你可以map readLine使用它们 - 你不需要readLines首先将它分成行.(你可以告诉它是一个列表,因为它是最右边的一个:.)

你说

然而,我认为正在发生的是,finish是列表中的顶级元素,并且start从该字符串分配了顶部char.

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

readLines :: String -> [Line]
Run Code Online (Sandbox Code Playgroud)

这意味着track必须是一个String - 这是readLines可以处理的唯一内容.

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

3:4:stuff
Run Code Online (Sandbox Code Playgroud)

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

c:"a string"
Run Code Online (Sandbox Code Playgroud)

意味着c必须是Char,因为String = [Char].

在你的代码中,我们已经计算出track一个String,这意味着你写的时候

(start : finish : track)
Run Code Online (Sandbox Code Playgroud)

开始和结束都必须是你可以放在String前面的元素,所以开始和结束都必须是Char.

然后Haskell查看你的代码readPoint start,但是因为它已经解决了start类型Char,但是

readPoint :: String -> Point
Run Code Online (Sandbox Code Playgroud)

它抱怨Char和String不匹配.

我认为你犯了错误,因为你忘了readLines只需要一个字符串,但它感觉(从名字中)就像它应该乐意接受一个字符串列表.你parseLines看起来像它做了类似的事情,但它需要一个字符串列表,所以科佩斯,而readlines方法需要用换行符一个字符串,因此不能以列表应付.