【F#从入门到实战】06. F#表达式化简
欢迎大家来到【F#从入门到实战】,在这里我将分享关于F#编程语言的系列文章,带大家一起去学习和成长,并探索函数编程语言F#这个有趣的世界。所有文章都会结合示例代码和笔者的经验进行讲解,真心想把十余年的IT经验分享给大家,希望对您有所帮助,文章中也定有不足之处,请海涵!本系统文章将从F#基本语法入手,逐步通过自定义类型来实现数学表达式的各种常见解析操作,如对表达式进行求值、化简、展开、求导和求积分等。此系统博文也是了解和实现一个简易的计算机代数系统的基础。
下面给出【F#从入门到实战】系统专题文章的目录:
【F#从入门到实战】01. F#语言快速入门
【F#从入门到实战】02. F#数组常见用法
【F#从入门到实战】03. F#自定义操作符
【F#从入门到实战】04. F#5.0新特征总结
【F#从入门到实战】05. F#表达式求值
【F#从入门到实战】06. F#表达式化简
【F#从入门到实战】07. F#表达式展开
【F#从入门到实战】08. F#大整数阶乘
【F#从入门到实战】09. F#表达式求导
【F#从入门到实战】10. F#表达式积分
【F#从入门到实战】11. F#库FParsec入门
【F#从入门到实战】12. F#库FParsec解析表达式
【F#从入门到实战】13. F#库FParsec实现求导符号计算
【F#从入门到实战】14. F#实现分部积分法
下面将正式开始本文的介绍:
如果使用过Matlab或者Maple等软件,应该知道这类数学软件的符号计算引擎非常强大,可以进行数学公式的推导,比如可以对数学公式进行化简。当然,实现一个功能完备的化简引擎还是不容易的。这里用F# 实现一个简单的化简函数。
首先定义一个表达式数据类型:
type Expr =
| CstF of float
| Var of string
| Add of Expr * Expr // +
| Sub of Expr * Expr // -
| Mul of Expr * Expr // *
| Div of Expr * Expr // /
其次定义一个化简函数,它是化简规则的具体实现:
(* 化简 *)
let rec simplify e =
match e with
| CstF f -> CstF f
| Var x -> Var x
| Add(CstF a, CstF b) -> CstF (a + b)
| Add(CstF 0., e2) -> simplify e2
| Add(e1 , CstF 0.) -> simplify e1
| Add(e1 , e2) when e1 = e2 -> simplify (Mul(CstF 2., simplify e1))
| Add(Mul(CstF a, e1) , Mul(CstF b, e2)) when e1 = e2 -> simplify (Mul(CstF (a + b), simplify e1))
| Add(e1,Sub(a,e2)) when e1 = e2 -> simplify a
| Add(Add(a, x1),Sub(b, x2)) when x1 = x2 -> simplify(Add(simplify a,simplify b))
| Add(e1, e2) -> Add(simplify e1,simplify e2)
| Mul(CstF a, CstF b) -> CstF (a * b)
| Mul(CstF 0., e2) -> CstF 0.
| Mul(e1 , CstF 0.) -> CstF 0.
| Mul(CstF 1., e2) -> simplify e2
| Mul(e1 , CstF 1.) -> simplify e1
| Mul(e1, e2) -> Mul(simplify e1,simplify e2)
| Sub(CstF a, CstF b) -> CstF (a - b)
| Sub(Add(e1,e2),e3) when e1 = e3 -> simplify e2
| Sub(Add(e1,e2),e3) when e2 = e3 -> simplify e1
| Sub(e1, e2) when e1 = e2 -> CstF 0.
| Sub(e1, e2) -> Sub(simplify e1,simplify e2)
| Div(CstF a, CstF b) -> CstF (a / b)
| Div(CstF 0., e2) -> CstF 0.
| Div(e1, e2) when e1 = e2 -> CstF 1.
| Div(e1, e2) -> Div(simplify e1,simplify e2)
| _ -> failwith "unknown operation"
这个化简函数,返回一个是一个自定义DSL的表达式结构,如Sub(Var "a", Var "x") ,下面再定义一个可以化简和打印出字符串的函数:
let rec simp e =
let res = simplify e
match res with
| CstF f -> string f
| Var x -> x
| Add(e1 , e2) -> "(" + (simp e1) + "+" + (simp e2) + ")"
| Sub(e1 , e2) -> "(" + (simp e1) + "-" + (simp e2) + ")"
| Mul(e1 , e2) -> "(" + (simp e1) + "*" + (simp e2) + ")"
| Div(e1 , e2) -> "(" + (simp e1) + "/" + (simp e2) + ")"
| _ -> failwith "unknown operation";;
最后,再定义一个打印DSL表达式的函数:
let rec printExpr e =
match e with
| CstF f -> string f
| Var x -> x
| Add(e1 , e2) -> "(" + (printExpr e1) + "+" + (printExpr e2) + ")"
| Sub(e1 , e2) -> "(" + (printExpr e1) + "-" + (printExpr e2) + ")"
| Mul(e1 , e2) -> "(" + (printExpr e1) + "*" + (printExpr e2) + ")"
| Div(e1 , e2) -> "(" + (printExpr e1) + "/" + (printExpr e2) + ")"
| _ -> failwith "unknown operation";;
至此,可以测试一下,如何化简数学表达式
//( a + x ) + ( a - x )
let e1 = Add(Add(Var "a", Var "x"),Sub(Var "a", Var "x"))
printExpr e1 + " => " + simp e1 ;;
- 点赞
- 收藏
- 关注作者
评论(0)