我正在学习函数式编程的入门课程,我们使用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)
您的变量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方法需要用换行符一个字符串,因此不能以列表应付.