我遇到了READPrec的实现问题,无法解析以下数据结构的输入:
data Term = Monom Int Int
| Addition Term Term
| Subtraction Term Term
| Multiplication Term Term
| Division Term Term
我已经实现了一个show的实例,它使monom看起来像:
let k = Monom 2 3
Main.show k
返回:
(2x^3)
和
let m = Addition k k
Main.show m
返回:
(2x^3)+(2x^3)
与此同时,我坐在这个任务的5个小时左右,我真的不知道如何处理它。我的第一种方法看起来像这样:
instance Read Term where
readsPrec _ inp = let[(a,b)] = lex inp in
case a of
"x" -> readsPrec 0 b
"^" -> [(Monom 1 (read b::Int), "")]
c -> let[(d, "")] = readsPrec 0 b in
[(Monom (read c::Int) ((\(Monom x y) -> y) d), "")]
起初我感到非常高兴,直到我注意到此代码不适用于除Monom以外的任何其他内容。任何人都有更好的方法吗?
答案 0 :(得分:2)
是。这可能看起来有点过于强大,但使用像Parsec这样的解析器组合库可以让你整齐地编写代码。 E.g。
import Text.ParserCombinators.Parsec
import Data.Maybe
monom, term :: Parser Term
operations :: [(Char,(Term -> Term -> Term))] -> Parser Term
int :: Parser Int
int = fmap read $ many1 digit
monom = do
coef <- int
string "x^"
power <- int
return $ Monom coef power
operations ops = do
a <- term
c <- choice . map (char . fst) $ ops
b <- term
return $ (fromJust $ lookup c ops) a b
term = do
char '('
x <- monom <|> (operations [('+', Addition), ('-', Subtraction), ('*', Multiplication), ('/', Division)])
char ')'
return x
term' = do
x <- term
eof
return x
readTerm :: String -> Term
readTerm string = case parse term' "" string of
Left err -> error . show $ err
Right term -> term
作为解释,monom
解析类似2x^3
(不带括号)的内容,operations
获取元组列表并解析term
后跟一个操作字符,然后是另一个term
,然后使用适当的数据构造函数来创建正确的实例(fromJust $ lookup c ops
行)。
term
解析器解析monom
或其中一个操作,用括号括起来。 term'
解析整个字符串(即确保解析器运行到字符串的末尾)。 readTerm
只是解析器的“清洁”版本。
一些例子:
> readTerm "(2x^3)"
Monom 2 3
> readTerm "((2x^3)+(2x^3))"
Addition (Monom 2 3) (Monom 2 3)
> readTerm "(((2x^3)+(2x^3))*(2x^3))"
Multiplication (Addition (Monom 2 3) (Monom 2 3)) (Monom 2 3)
以上是一个非常基本的版本,可以很容易地扩展到(例如)使coef
术语可选,以便x^2
解析为Monom 1 2
,或者使{ {1}}术语可选,以便power
解析为2x
。 (option
函数对于此特定修改非常有用,并且只增加1或2行。)
(注意:这可能是更有效和优雅的应用风格,例如
Monom 2 1
但是在进行修改时这可能会有点不合适。)