F#表达式展开

简介: 数学软件Matlab或者Maple等软件可以对数学表达式进行展开操作,如Matlab软件中可以用 expand((x-2)*(x-4))对表达式(x-2)*(x-4)进行展开,即x^2 - 6*x + 8。那么这种抽象的数学表达式是如何实现的呢?本文就用F#语言来简单的讲解一下思路。实现expand(sin(3+x)-sin(3-x)) => 2*cos(3)*sin(x) 。

     数学软件Matlab或者Maple等软件可以对数学表达式进行展开操作,如Matlab软件中可以用 expand((x-2)*(x-4))对表达式(x-2)*(x-4)进行展开,即x^2 - 6*x + 8。那么这种抽象的数学表达式是如何实现的呢?下面我们就用F#语言来简单的讲解一下思路。当然,如果要实现完备的表达式,内部还是有非常多的细节需要处理,这里只讲解一下思路,并不构建一个完备的表达式展开函数。

    首先,我们需要将常见的数学上展开的公式进行罗列,这里处理如下表达式:

(a+x)*(b+x) =>ab+a*x+b*x+x^2sin(a+b)=sin(a)*cos(b)+cos(a)*sin(b)
sin(a-b)=sin(a)*cos(b)-cos(a)*sin(b)
cos(a+b)=cos(a)*cos(b)-sin(a)*sin(b)
cos(a+b)=cos(a)*cos(b)+sin(a)*sin(b)

   有了这些表达式展开规则后,可以构建F# expand函数,对表达式进行展开处理。下面定义一个Expr类型:

typeExpr=|CstFoffloat|Varofstring|AddofExpr*Expr// +|SubofExpr*Expr// -|MulofExpr*Expr// *|DivofExpr*Expr// / |PowofExpr*Expr// ^ |SinofExpr|CosofExpr;;

    下面定义一个expand 函数,它也是一个递归函数,用rec关键字来修饰expand函数:

letrecexpande=matchewith|CstFf->CstFf|Varx->Varx|Add(CstFa, CstFb) ->CstF (a+b)  
|Add(CstF0., e2) ->expande2|Add(e1 , CstF0.) ->expande1|Add(e1 , e2) ->Add(expand (e1),expand (e2))
|Sub(e1 , e2) ->Sub(expand (e1),expand (e2))
|Mul(CstFa, CstFb) ->CstF (a*b)   
|Mul(CstF0., e2) ->CstF0.|Mul(e1 , CstF0.) ->CstF0.|Mul(CstF1., e2) ->expande2|Mul(e1 , CstF1.) ->expande1|Mul(Add(e1,e2),Add(e3,e4)) ->Add(Add(expand (Mul(e1,e3)),expand (Mul(e2,e4))),Add(expand (Mul(e1,e4)),expand (Mul(e2,e3))))
|Mul(Sub(e1,e2),Sub(e3,e4)) ->Sub(Add(expand (Mul(e1,e3)),expand (Mul(e1,e4))),Add(expand (Mul(e1,e4)),expand (Mul(e2,e3))))
|Mul(Add(e1,e2),Sub(e3,e4)) ->Add(Add(expand (Mul(e1,e3)),expand (Mul(e2,e3))),Sub(expand (Mul(e1,e4)),expand (Mul(e2,e4))))
|Mul(Sub(e1,e2),Add(e3,e4)) ->Sub(Add(expand (Mul(e1,e3)),expand (Mul(e1,e4))),Add(expand (Mul(e2,e3)),expand (Mul(e2,e4))))
|Pow(e1,CstF1.) ->expande1|Pow(e1,CstFa) whena>1.->expand (Mul(e1,expand(Pow(e1,CstF (a-1.)))))
|Sin(Add(a,b)) ->expand (Add(Mul(Sin(a),Cos(b)),Mul(Cos(a),Sin(b)))) //sin(a+b)=sinacosb+cosasinb|Sin(Sub(a,b)) ->expand (Sub(Mul(Sin(a),Cos(b)),Mul(Cos(a),Sin(b)))) //sin(a-b)=sinacosb-sinbcosa|Cos(Add(a,b)) ->expand (Sub(Mul(Cos(a),Cos(b)),Mul(Sin(a),Sin(b))))
|Cos(Sub(a,b)) ->expand (Add(Mul(Cos(a),Cos(b)),Mul(Sin(a),Sin(b))))
|e->e;;

  另外,可以定义一个表达式化简函数simplify,如下所示:

letrecsimplifye=matchewith|CstFf->CstFf|Varx->Varx|Add(CstFa, CstFb) ->CstF (a+b)  
|Add(CstF0., e2) ->simplifye2|Add(e1 , CstF0.) ->simplifye1|Add(e1 , e2) whene1=e2->simplify (Mul(CstF2., simplifye1))
|Add(Mul(CstFa, e1) , Mul(CstFb, e2)) whene1=e2->simplify (Mul(CstF (a+b), simplifye1))
|Add(Mul(CstFa, e1) , Mul(e2,CstFb)) whene1=e2->simplify (Mul(CstF (a+b), simplifye1))
|Add(Mul(e1, CstFa) , Mul(e2,CstFb)) whene1=e2->simplify (Mul(CstF (a+b), simplifye1))
|Add(Mul(e1, CstFa) , Mul(CstFb, e2)) whene1=e2->simplify (Mul(CstF (a+b), simplifye1))
|Add(Mul(e1, e2) , Mul(e3, e4)) whene1=e4&&e2=e3->simplify (Mul(CstF2., simplify (Mul(e1,e3))))  //add|Add(e1,Sub(a,e2))  whene1=e2->simplifya|Add(Add(a, x1),Sub(b, x2)) whenx1=x2->simplify(Add(simplifya,simplifyb))
|Add(e1, e2) ->Add(simplifye1,simplifye2)
|Mul(CstFa, CstFb) ->CstF (a*b)   
|Mul(CstF0., e2) ->CstF0.|Mul(e1 , CstF0.) ->CstF0.|Mul(CstF1., e2) ->simplifye2|Mul(e1 , CstF1.) ->simplifye1|Mul(e1, e2) ->Mul(simplifye1,simplifye2)
|Mul(Varx, Vary) whenx=y->Pow(Varx, CstF2.)
|Mul(Varx, Pow(Vary,CstFa)) whenx=y->Pow(Varx, CstF (a+1.))
|Mul(Pow(Varx,CstFa), Pow(Vary,CstFb)) whenx=y->Pow(Varx, CstF (a+b))
|Sub(CstFa, CstFb)  ->CstF (a-b) 
|Sub(Add(e1, e2),Sub(e3, e4)) whene1=e3->simplify (Add(e2,e4)) 
|Sub(Add(e1,e2),e3) whene1=e3->simplifye2|Sub(Add(e1,e2),e3) whene2=e3->simplifye1|Sub(e1, e2) whene1=e2->CstF0.|Sub(e1, e2) ->Sub(simplifye1,simplifye2)
|Div(CstFa, CstFb)  ->CstF (a/b) 
|Div(CstF0., e2) ->CstF0.|Div(e1, e2) whene1=e2->CstF1.|Div(e1, e2) ->Div(simplifye1,simplifye2)
|e->e;;

   最后,需要一个将DSL数据类型翻译为字符串,打印到控制台,如下所示:

letrecprintExpre=matchewith|CstFf->stringf|Varx->x|Add(e1 , e2) ->""+ (printExpre1) +"+"+ (printExpre2) +""|Sub(Add(e1,e2) , Sub(e3,e4)) ->"("+ (printExpr (Add(e1,e2))) +")-("+ (printExpr (Sub(e3,e4))) +")"|Sub(Add(e1,e2) , Add(e3,e4)) ->"("+ (printExpr (Add(e1,e2))) +")-("+ (printExpr (Add(e3,e4))) +")"|Sub(e1 , e2) ->""+ (printExpre1) +"-"+ (printExpre2) +""|Mul(e1 , Add(e2,e3)) ->""+ (printExpre1) +"*("+ (printExpr (Add(e2,e3))) +")"|Mul(e1 , Sub(e2,e3)) ->""+ (printExpre1) +"*("+ (printExpr (Sub(e2,e3))) +")"|Mul(e1 , e2) ->""+ (printExpre1) +"*"+ (printExpre2) +""|Div(e1 , e2) ->""+ (printExpre1) +"/"+ (printExpre2) +""|Pow(e1 , e2) ->""+ (printExpre1) +"^"+ (printExpre2) +""|Sin(e1) ->"sin("+ (printExpre1) +")"|Cos(e1) ->"cos("+ (printExpre1) +")"|_->failwith"unknown operation";;

   注意:这里有些规则并为经过严格测试,并不保证所有的规则都正确。

  至此,一个简单的F#表达式展开函数就完成了。下面给出一个示例:

lete1=Mul(Add(CstF3., Var"x"),Add(CstF2., Var"x")) ;;
lete2=expande1;;
printExpr (simplifye2);;

给出一个复合三角函数展开示例:

lete1=Sub(Sin(Add(CstF3., Var"x")),Sin(Sub(CstF3., Var"x"))) ;; //sin(3+x)-sin(3-x)lete2=expande1;;
printExpr (simplifye2);;

1628574321590046518.jpg

即 expand(sin(3+x)-sin(3-x)) => 2*cos(3)*sin(x)

相关文章
|
7月前
|
C语言 C++
操作符的属性:优先级、结合性(缺表达式求值)
操作符的属性:优先级、结合性(缺表达式求值)
50 0
|
7月前
|
JavaScript 前端开发 Java
运算符与逻辑关系,待编辑
运算符与逻辑关系,待编辑
|
7月前
|
测试技术
leetcode-241:为运算表达式设计优先级
leetcode-241:为运算表达式设计优先级
39 0
|
7月前
三元表达式使用方法以及如何使用三元表达式回显单选框
三元表达式使用方法以及如何使用三元表达式回显单选框
41 0
|
7月前
第二章:数据类型、运算符和表达式
第二章:数据类型、运算符和表达式
59 0
|
算法
表达式转换-中缀转后缀表达式后计算-数据结构与算法
表达式转换-中缀转后缀表达式后计算-数据结构与算法
403 0
表达式转换-中缀转后缀表达式后计算-数据结构与算法
|
自然语言处理 Java
Antlr实现任意四则运算表达式求值
上面语法就是四则运算的巴科斯范式定义(EBNF),可能对初学者有点难理解,其实就是一个递归定义,一个表达式可能是有多个子表达式构成,但子表达式的尽头一定是数字。 antlr可以用EBNF所定义的规则,将某个输入串解析为一颗抽象语法树(AST)。我们以表达式((3+3)*(1+4))/(5-3) 为例
178 0
|
算法
6.解析表达式算法
6.解析表达式算法
114 0
|
存储 Unix 编译器
表达式求值过程中会发生哪些隐藏的变化?求值顺序又由什么决定?——详解C表达式求值中的隐式类型转换,算术转换问题,以及操作符的属性
表达式求值过程中会发生哪些隐藏的变化?求值顺序又由什么决定?——详解C表达式求值中的隐式类型转换,算术转换问题,以及操作符的属性
169 0