recursion - 数学表达式简化 f#

标签 recursion f# f#-interactive

我正在研究一个数学表达式简化器(相当基本,没有指数、对数、根、分数等),并且我基本上可以使用它。在我使用的 19 项测试中,其中 14 项通过了。对于剩下的 5 个,我在简化函数中编写了多个其他语句,但它似乎没有改变任何事情。

下面是代码(复制并粘贴到解释器中,它工作得很好)

    //expression types 
type Expression =
    | X
    | Y
    | Const of float
    | Neg of Expression
    | Add of Expression * Expression
    | Sub of Expression * Expression
    | Mul of Expression * Expression

// formats string 
let exprToString expr =
    let rec recExprStr parens expr =
        let lParen = if parens then "(" else ""
        let rParen = if parens then ")" else ""
        match expr with
        | X -> "x"
        | Y -> "y"
        | Const n -> n.ToString()
        | Neg e -> lParen + "-" + recExprStr true e + rParen
        | Add (e1, e2) -> lParen + recExprStr true e1 + "+" + recExprStr true e2 + rParen
        | Sub (e1, e2) -> lParen + recExprStr true e1 + "-" + recExprStr true e2 + rParen
        | Mul (e1, e2) -> lParen + recExprStr true e1 + "*" + recExprStr true e2 + rParen
    recExprStr false expr

//simplification function 
let rec simplify expr =
    match expr with
    //addition 
    | Add(Const(ex1), Const(ex2)) -> Const(ex1 + ex2)
    | Add(ex1, Const(0.)) -> ex1 |> simplify
    | Add(Const(0.), ex1) -> ex1 |> simplify
    | Add(Const(num), ex1) -> Add(ex1, Const(num)) |> simplify
    | Add(ex1, Neg(ex2)) -> Sub(ex1, ex2) |> simplify
    | Add(Neg(ex1), ex2) -> Sub(ex2, ex1) |> simplify
    //subtraction 
    | Sub(Const(num1), Const(num2)) -> Const(num1 - num2)
    | Sub(ex1, Const(0.)) -> ex1 |> simplify
    | Sub(Const(0.), ex1) -> Neg(ex1) |> simplify
    //multiplication 
    | Mul(Const(num1), Const(num2)) -> Const(num1 * num2)
    | Mul(ex1, Const(1.)) -> ex1 |> simplify
    | Mul(Const(1.), ex1) -> ex1 |> simplify
    | Mul(ex1, Const(0.)) -> Const(0.)
    | Mul(Const(0.), ex1) -> Const(0.)
    | Mul(ex1, Const(num1)) -> Mul(Const(num1), ex1) |> simplify
    | Mul(Neg(ex1), ex2) -> Neg(Mul(ex1, ex2)) |> simplify
    | Mul(ex1, Neg(ex2)) -> Neg(Mul(ex1, ex2)) |> simplify
    //negation involving a number
    | Neg(Const(0.)) -> Const(0.)
    | Neg(Neg(ex1)) -> ex1 |> simplify
    | _ -> expr

//Tests
printfn "---Provided Tests---"
let t1 = Add (Const 5.0, Const 3.0)
let t2 = Sub (Const 5.0, Const 3.0)
let t3 = Mul (Const 5.0, Const 3.0)
let t4 = Neg (Const 4.0)
let t5 = Neg (Const -9.0)
let t6 = Add (X, Const 0.0)
let t7 = Add (Const 0.0, Y)
let t8 = Sub (X, Const 0.0)
let t9 = Sub (Const 0.0, Y)
let t10 = Sub (Y, Y)
let t11 = Mul (X, Const 0.0)
let t12 = Mul (Const 0.0, Y)
let t13 = Mul (X, Const 1.0)
let t14 = Mul (Const 1.0, Y)
let t15 = Neg (Neg X)
let t16 = Sub (Mul (Const 1.0, X), Add (X, Const 0.0))
let t17 = Add (Mul (Const 4.0, Const 3.0), Sub (Const 11.0, Const 5.0))
let t18 = Sub (Sub (Add (X, Const 1.0), Add (X, Const 1.0)), Add (Y, X))
let t19 = Sub (Const 0.0, Neg (Mul (Const 1.0, X)))

//Output goes here! 
//5 + 3 = 0
printfn "t1  Correct: 8\t\tActual: %s" (exprToString (simplify t1))
//5-3 = 2
printfn "t2  Correct: 2\t\tActual: %s" (exprToString (simplify t2))
//5 * 3 = 15
printfn "t3  Correct: 15\t\tActual: %s" (exprToString (simplify t3))
//-(4) = -4
printfn "t4  Correct: -4\t\tActual: %s" (exprToString (simplify t4))
//-(-9) = 9
printfn "t5  Correct: 9\t\tActual: %s" (exprToString (simplify t5))
//x + 0 = x
printfn "t6  Correct: x\t\tActual: %s" (exprToString (simplify t6))
//0 + y = y
printfn "t7  Correct: y\t\tActual: %s" (exprToString (simplify t7))
//x - 0 = x
printfn "t8  Correct: x\t\tActual: %s" (exprToString (simplify t8))
//0 - y = -y
printfn "t9  Correct: -y\t\tActual: %s" (exprToString (simplify t9))
//y - y = 0
printfn "t10  Correct: 0\t\tActual: %s" (exprToString (simplify t10))
//x * 0 = 0
printfn "t11  Correct: 0\t\tActual: %s" (exprToString (simplify t11))
//0 * y = 0
printfn "t12  Correct: 0\t\tActual: %s" (exprToString (simplify t12))
//x * 1 = x
printfn "t13  Correct: x\t\tActual: %s" (exprToString (simplify t13))
//1 * y = y
printfn "t14  Correct: y\t\tActual: %s" (exprToString (simplify t14))
//-(-x) = x
printfn "t15  Correct: x\t\tActual: %s" (exprToString (simplify t15))
// (1 * x) - (x + 0) = 0
printfn "t16  Correct: 0\t\tActual: %s" (exprToString (simplify t16))
//(4 * 3) + (11 - 5) = 18
printfn "t17  Correct: 18\t\tActual: %s" (exprToString (simplify t17))
// ((x + 1) - (x + 1)) - (y+x) = -y -x
printfn "t18  Correct: -y -x\t\tActual: %s" (exprToString (simplify t18))
// 0 - (-(1 * x)) = x
printfn "t19  Correct: x\t\tActual: %s" (exprToString (simplify t19))
// (x + 1) *  (-2y + x) 

程序的输出如下,我标记了6个失败的测试(正确的是正确答案,实际的是我返回的结果)

t1  Correct: 8          Actual: 8
t2  Correct: 2          Actual: 2
t3  Correct: 15         Actual: 15
t4  Correct: -4         Actual: -4
t5  Correct: 9          Actual: --9 //FAILS 
t6  Correct: x          Actual: x
t7  Correct: y          Actual: y
t8  Correct: x          Actual: x
t9  Correct: -y         Actual: -y
t10  Correct: 0         Actual: y-y //FAILS
t11  Correct: 0         Actual: 0
t12  Correct: 0         Actual: 0
t13  Correct: x         Actual: x
t14  Correct: y         Actual: y
t15  Correct: x         Actual: x
t16  Correct: 0         Actual: (1*x)-(x+0)          //FAILS
t17  Correct: 18                Actual: (4*3)+(11-5) //FAILS
t18  Correct: -(y + x)             Actual: ((x+1)-(x+1))-(y+x) //FAILS 
t19  Correct: x         Actual: x

我对如何解决最后的 4 (16,17,18) 有点困惑,但在我看来,#5 和 #10 应该可行。

对于测试 5,我添加了 | Neg(Neg(ex1)) -> ex1 |> Simply 我认为它会捕获我的双重否定,但没有。

对于测试 10,我想到了类似 | 的东西。 Sub(ex1, ex2) -> (ex1 - ex2) 可以工作,但事实证明这甚至不是有效的语法。

我已经浏览了大约六个有关简化的资源,甚至复制并粘贴了他们的一些工作,我的测试仍然失败。它知道我一定错过了一两个案例,但我正在抓着我的头发试图弄清楚我可能遗漏了什么!我非常感谢任何意见!

(原始帖子有一个测试 20,出于简化答案的目的,我已将其删除。鉴于我当前的代码,我意识到我不可能简化它)

最佳答案

5:Neg(Const -9) 没有得到简化。它不是负数的负数。您需要一个规则 Neg(Const x) -> Const(-x) 来替换 Neg(Const 0.) 规则。

10: Sub(x,y) 当 y = x -> Const(0)

16:你没有简化内部部件。我会在简化外部部件之前这样做。例如。 Sub(x,y) -> 让 x',y' = 简化 x, 简化 y 然后将 x',y' 与...匹配

17:16 的解决方案可以解决此问题。

18:10 和 16 的解决方案可以解决此问题。

我也忍不住建议 let t = [Add (Const 5.0, Const 3.0); Sub (Const 5.0, Const 3.0)...] 然后 t |> List.iteri ...

我正在使用一个临时的代数简化器,它的效果还不错,但我想创建一个更严肃的代数简化器。如果有人对这方面的工作非常感兴趣,请告诉我。

关于recursion - 数学表达式简化 f#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49099936/

相关文章:

javascript - 如何在javascript中使用递归代码在对象中创建内部对象

c - codechef 练习题中的运行时错误

Scala 等价于 F# 中的 |> 或 Clojure 中的 ->>

F# Interactive - 如何让它打印对象的属性,当后者是可枚举的

f# - 是否可以将 #load 指令放在 F# fsx 文件中的 #if 指令中?

python - 递归地在每个深度绘制带有颜色的谢尔宾斯基三角形?

java - 在Java中创建最小二叉搜索树(BST)

f# - 异步计算无法捕获 OperationCanceledException

f# - 如何编写中缀函数

asynchronous - 为什么 Async.Sequential 不能按预期工作(但在 FSI 中工作)?