[英]Haskell: How to define my custom math data type recursively (stop infinite recursion)
我正在定义一个自定义数据类型,以帮助我进行项目的演算。 我已将数据类型定义如下:
data Math a =
Add (Math a) (Math a)
| Mult (Math a) (Math a)
| Cos (Math a)
| Sin (Math a)
| Log (Math a)
| Exp (Math a)
| Const a
| Var Char
deriving Show
我正在创建一个名为eval
,该函数为我部分评估数学表达式。 这就是我所拥有的:
eval (Const a) = (Const a)
eval (Var a) = (Var a)
eval (Add (Const a) (Const b)) = eval (Const (a+b))
eval (Add (Var a) b) = eval (Add (Var a) (eval b))
eval (Add a (Var b)) = eval (Add (eval a) (Var b))
eval (Add a b) = eval (Add (eval a) (eval b))
eval (Mult (Const a) (Const b)) = (Const (a*b))
eval (Mult a (Var b)) = (Mult (eval a) (Var b))
eval (Mult (Var a) b) = (Mult (Var a) (eval b))
eval (Mult a b) = eval (Mult (eval a) (eval b))
eval (Cos (Const a)) = (Const (cos(a)))
eval (Cos (Var a)) = (Cos (Var a))
eval (Cos a) = eval (Cos (eval a))
eval (Sin (Const a)) = (Const (sin(a)))
eval (Sin (Var a)) = (Sin (Var a))
eval (Sin a) = eval (Sin (eval a))
eval (Log (Const a)) = (Const (log(a)))
eval (Log (Var a)) = (Log (Var a))
eval (Log a) = eval (Log (eval a))
eval (Exp (Const a)) = (Const (exp(a)))
eval (Exp (Var a)) = (Exp (Var a))
这在大多数情况下都可以正常工作。 例如, eval (Mult ((Const 4)) (Add (Cos (Const (0))) (Log (Const 1))))
结果为(Const 4.0)
每当我给变量添加两个常量时,就会出现我的问题: eval (Add (Const 4) (Add (Const 4) (Var 'x')))
给我无限递归。 我确定问题是因为我在eval (Add ab) = eval (Add (eval a) (eval b))
上调用了eval
。 如果我做这行, eval (Add ab) = (Add (eval a) (eval b))
,我将停止无限递归,但是我不再简化我的答案: Add (Const 4.0) (Add (Const 4.0) (Var 'x'))
产生完全相同的Add (Const 4.0) (Add (Const 4.0) (Var 'x'))
。 如何获得类似Add (Const 8.0) (Var 'x')
?
任何帮助将非常感激!
您的问题是您不会将已经简化的表达式与未简化的表达式区别对待。 您一直调用eval
直到两个操作数都是常量为止,如果这永远不会发生,就永远不会终止。 最简单的有问题的输入将是Add (Var "x") (Const 5)
。 这样的输入应该结束递归并返回自身。 但是相反,它将继续在相同的输入上调用eval
:
eval (Add (Var "x") (Const 5))
= eval (Add (Var "x") (eval (Const 5)))
= eval (Add (Var "x") (Const 5))
= eval (Add (Var "x") (eval (Const 5)))
= ... ad infinitum
通常,首先避免此类问题的方法(即,在缺少基本案例的情况下使其显而易见)是按照以下方式构造函数:所有函数的递归案例仅使用sub来调用-参数表达式的表达式。
在这种情况下,可以通过先对操作数求值,然后对结果进行恒定折叠而无需其他递归调用来实现。 看起来像这样:
eval (Add a b) =
case (eval a, eval b) of
(Const x, Const y) => Const (x+y)
(x, y) => Add x y
在这里,唯一的递归调用是eval a
和eval b
,其中a
和b
是原始表达式的子表达式。 这样可以保证终止,因为如果所有情况都遵循此规则,则最终将到达没有子表达式的表达式,这意味着递归必须终止。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.