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)

相关文章
|
9月前
|
JavaScript 前端开发 Java
|
3月前
|
Arthas 监控 Java
-x [展开层数] 的详细用法和举例?
-x [展开层数] 的详细用法和举例?
41 5
|
6月前
|
存储 JavaScript 前端开发
展开运算符的介绍使用(...),实际应用this.tableData.push({...})
这篇文章介绍了ES6中引入的展开运算符`(...)`的多种用途,包括数组合并与复制、对象合并与复制、函数参数的展开以及字符串处理,并强调了它在简化代码、提高开发效率方面的重要性,同时通过实际代码示例展示了其在项目中的应用。
|
7月前
|
JavaScript
Vue插值表达式数组写法,插值表达式,如何插入数据,数组,集合,{{}}多项数据显示页面的写法
Vue插值表达式数组写法,插值表达式,如何插入数据,数组,集合,{{}}多项数据显示页面的写法
|
9月前
|
C#
C#动态查询:巧用Expression组合多条件表达式
在C#中,利用`Expression`类和`AndAlso`、`OrElse`方法,可以组合两个`Expression<Func<T, bool>>`以实现动态多条件查询。该方法通过构建表达式树,方便地构建复杂查询。示例代码展示了如何创建表达式树,分别检查年龄大于等于18和姓名为"John"的条件,并使用`AndAlso`组合这两个条件,最终编译为可执行的委托进行测试。
382 1
|
7月前
|
语音技术 数据安全/隐私保护
语音识别,猜猜心里数字讲解,猜数字的组合,判断语句的嵌套,嵌套语句使用很简单,我们写一个外层嵌套的条件,利用缩进,满足条件,才会执行条件2,判断语句综合案例,如何产生变量的随机数字,while循环应用
语音识别,猜猜心里数字讲解,猜数字的组合,判断语句的嵌套,嵌套语句使用很简单,我们写一个外层嵌套的条件,利用缩进,满足条件,才会执行条件2,判断语句综合案例,如何产生变量的随机数字,while循环应用
|
9月前
|
存储 程序员 编译器
【新手解答5】深入探索 C 语言:宏中的文本、标识符和字符串 + 递归运算、条件语句、循环 + `switch-case` 与多项条件和枚举的差别
【新手解答5】深入探索 C 语言:宏中的文本、标识符和字符串 + 递归运算、条件语句、循环 + `switch-case` 与多项条件和枚举的差别
98 0
|
存储 Unix 编译器
表达式求值过程中会发生哪些隐藏的变化?求值顺序又由什么决定?——详解C表达式求值中的隐式类型转换,算术转换问题,以及操作符的属性
表达式求值过程中会发生哪些隐藏的变化?求值顺序又由什么决定?——详解C表达式求值中的隐式类型转换,算术转换问题,以及操作符的属性
185 0
|
Linux iOS开发
查看宏展开后的代码
查看宏展开后的代码
687 0
Object展开符
Object展开符
93 0

热门文章

最新文章