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

时间:2012-09-17 20:53:12

标签: list haskell

我正在学习函数式编程的入门课程,我们使用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从该字符串中分配了顶部字符。

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

非常感谢!

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)

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)

2 个答案:

答案 0 :(得分:7)

您的变量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)

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

然后Haskell会查看您的代码readPoint start,但是因为start的类型为Char,但是

readPoint :: String -> Point

它抱怨Char和String不匹配。

我认为你犯了这个错误,因为你忘了readLines只接受一个字符串,但感觉(从名字中)就像它应该乐意接受一个字符串列表。你的parseLines看起来像它做了类似的事情,但它需要一个字符串列表,所以cope,而readLines需要一个带换行符的字符串,所以无法处理列表。

答案 1 :(得分:0)

<强> UPD 即可。哦,抱歉,我没有得到track表示多个曲目,必须是[String]类型。所以AndrewC的答案更合适。

因为在haskell中,模式(x:xs)表示如果x的类型为a,那么xs必须属于[a]类型{{1}类似于parseTrack类型的smth。
编译器想评估类型(a : a : [a]),首先它在右边看到的是a。 Func readLines track的类型为readLines,因此编译器String -> [Line]track,这意味着String的类型为[a]。同样在haskell中StringString,因此[Char]a
但您需要Char作为a。所以你只需要先取三个字符串然后抛出String的其余尾部。在类型中,它意味着像[String]那样的smth。为此,您可以将(String : String : String : [String])中的匹配模式重写为:

parseTrack