我编写了以下 Haskell 程序来解释基本数学。除了数学运算符之外,我还想添加比较和布尔运算符。我的问题是我应该如何用可以处理or的Int
东西替换出现的.Int
Bool
我考虑将Token
类型扩展为具有三种类型的运算符,它们仅在函数的类型((Int -> Int -> Int)
、、(Int -> Int -> Bool)
和)上有所不同(Bool -> Bool -> Bool)
,但这似乎会导致在类型声明和模式匹配。有没有办法用类型类来做到这一点?
type Precedence = Int
data Associativity = AssocL | AssocR
data Token = Operand Int | Operator String (Int -> Int -> Int) Associativity Precedence | ParenL | ParenR
instance Eq Token where
Operator s1 _ _ _ == Operator s2 _ _ _ = s1 == s2
Operand x1 == Operand x2 = x1 == x2
ParenL == ParenL = True
ParenR == ParenR = True
_ == _ = False
evalMath :: String -> Int
evalMath = rpn . shuntingYard . tokenize
tokenize :: String -> [Token]
tokenize = map token . words
where token s@"+" = Operator s (+) AssocL 2
token s@"-" = Operator s (-) AssocL 2
token s@"*" = Operator s (*) AssocL 3
token s@"/" = Operator s div AssocL 3
token s@"^" = Operator s (^) AssocR 4
token "(" = ParenL
token ")" = ParenR
token x = Operand $ read x
shuntingYard :: [Token] -> [Token]
shuntingYard = finish . foldl shunt ([], [])
where finish (tokens, ops) = (reverse tokens) ++ ops
shunt (tokens, ops) token@(Operand _) = (token:tokens, ops)
shunt (tokens, ops) token@(Operator _ _ _ _) = ((reverse higher) ++ tokens, token:lower)
where (higher, lower) = span (higherPrecedence token) ops
higherPrecedence (Operator _ _ AssocL prec1) (Operator _ _ _ prec2) = prec1 <= prec2
higherPrecedence (Operator _ _ AssocR prec1) (Operator _ _ _ prec2) = prec1 < prec2
higherPrecedence (Operator _ _ _ _) ParenL = False
shunt (tokens, ops) ParenL = (tokens, ParenL:ops)
shunt (tokens, ops) ParenR = ((reverse afterParen) ++ tokens, tail beforeParen)
where (afterParen, beforeParen) = break (== ParenL) ops
rpn :: [Token] -> Int
rpn = head . foldl rpn' []
where rpn' (x:y:ys) (Operator _ f _ _) = (f x y):ys
rpn' xs (Operand x) = x:xs