繁体   English   中英

Haskell:如何递归定义我的自定义数学数据类型(停止无限递归)

[英]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 aeval b ,其中ab是原始表达式的子表达式。 这样可以保证终止,因为如果所有情况都遵循此规则,则最终将到达没有子表达式的表达式,这意味着递归必须终止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM