为什么这种模式无法反驳?

时间:2017-03-02 18:03:35

标签: haskell functional-programming pattern-matching

我是Haskell的新手,并尝试创建一个评估算术表达式的函数:

data Expr = Number Rational | Variable String | Plus Expr Expr deriving (Eq, Show , Read)

eval (Plus lhs rhs) = Number (a + b)
  where Number a = eval lhs
    Number b = eval rhs
eval x = x

main = putStrLn . show $ eval (Plus (Number 1) (Variable "x"))

我希望eval函数在表达式Variable之前评估表达式,这意味着只要Plus中的两个表达式计算为Number,就可以将其折叠为Number一个新的Plus,否则您无法折叠Plus,但也许您只能折叠Number b的一侧。例如:eval(加号(数字a)(数字b))==数字(a + b)eval(加号(Var" a")b)==加号(Var" a&#34 ;)(评估b)

我预计模式eval x = x会失败并继续main: main.hs:5:9-27: Irrefutable pattern failed for pattern Main.Number b 并按原样返回表达式,但它会产生错误: with cte as ( select * , rn = row_number() over ( partition by cltid, Invnum order by [date] desc ) from a ) select cltid, Invnum, Cash, [date] from cte where rn = 1

为什么会发生这种情况,我该如何解决?

2 个答案:

答案 0 :(得分:4)

您需要使用case表达式来匹配内部eval调用的所有可能结果,而不仅仅是Number s:

eval (Plus lhs rhs) = case (eval lhs, eval rhs) of
  (Number a, Number b) -> Number (a + b)     -- two numbers can be summed
  (otherA,   otherB)   -> Plus otherA otherB -- other values form a Plus with collapsed parts
eval x              = x

答案 1 :(得分:3)

您可以使用ViewPatterns使其相对漂亮:

{-# LANGUAGE ViewPatterns #-}
eval (Plus (eval -> Number a) (eval -> Number b)) = Number (a+b)
eval x = x

鉴于你的评论,如果我没有指出你提出的算法存在一些缺陷,我会失职;例如:

> eval (Number 3 `Plus` Number 4 `Plus` Variable "a") -- doesn't know about associativity of Plus
Plus (Plus (Number (3 % 1)) (Number (4 % 1))) (Variable "a")
> eval (Variable "a" `Plus` (Number 3 `Plus` Number 4)) -- doesn't simplify recursively when it hits a variable
Plus (Variable "a") (Plus (Number (3 % 1)) (Number (4 % 1)))

考虑规范化为较不结构化的类型;或许类似

data Sum = Sum Rational (MultiSet String)

告诉第一部分中所有Number的总和以及每个变量在the second part中出现的频率。然后

eval :: Expr -> Sum
eval (Number r) = Sum r empty
eval (Variable v) = Sum 0 (singleton v)
eval (Plus l r) = Sum (r+r') (union vs vs') where
    Sum r  vs  = eval l
    Sum r' vs' = eval r