4

我正在学习关于函数式编程的入门课程,我们使用 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)

类型.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)
4

2 回答 2

8

您的变量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先将其分成几行。(你可以说它是一个列表,因为它是 a 右侧的最后一件事:。)

你说

然而,我认为正在发生的是,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)

start 和 finish 都必须是可以放在字符串前面的元素,因此 start 和 finish 都必须是 Char。

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

readPoint :: String -> Point

它抱怨 Char 和 String 不匹配。

我认为您犯了错误是因为您忘记了 readLines 采用单个字符串,但感觉(从名称上)它应该愉快地采用字符串列表。您的 parseLines 看起来像是在做类似的事情,但是它需要一个字符串列表,因此可以应付,而 readLines 需要一个带有换行符的字符串,因此无法应付列表。

于 2012-09-17T21:38:38.217 回答
0

升级版。哦,对不起,我没明白这track意味着多轨,必须是 type [String]。所以 AndrewC 的回答更合适。

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

parseTrack (start : finish : track : _)
于 2012-09-17T21:33:43.163 回答