我正在学习函数式编程的入门课程,我们使用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)
答案 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中String
为String
,因此[Char]
为a
。
但您需要Char
作为a
。所以你只需要先取三个字符串然后抛出String
的其余尾部。在类型中,它意味着像[String]
那样的smth。为此,您可以将(String : String : String : [String])
中的匹配模式重写为:
parseTrack