数学软件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);;
即 expand(sin(3+x)-sin(3-x)) => 2*cos(3)*sin(x)