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)

相关文章
|
5月前
|
人工智能 自然语言处理 数据可视化
2025年适合大型企业的BI产品及企业高效运用BI工具指南
本文将聚焦适合大型企业的BI产品选型,深入解析主流产品优势,并结合实践经验探讨企业如何用好BI工具,为各行业企业提供专业参考。
|
3月前
|
人工智能 调度 对象存储
阿里云人工智能平台PAI免费试用:DSW、EAS和DLC免费政策、查询及领取全流程
阿里云PAI平台推出2026年最新免费试用政策,涵盖DSW、EAS和DLC三大服务。新用户可领取独立试用资源:DSW享750计算时(3个月),EAS获500元抵扣金(1个月),DLC提供100计算时(3个月)。需先领后用,额度用尽或到期未停服将转按量计费。支持多地域与多种规格,建议及时停止实例避免额外费用。详情及领取入口见官方页面。
|
Ubuntu 数据安全/隐私保护
Ubuntu下安装clickhouse
1、创建/etc/apt/sources.list.d/clickhouse.list文件, 2、在clickhouse.list文件里写入deb http://repo.yandex.ru /clickhouse/deb/stable/ main/ 3、一定要在clickhouse.
3775 0
|
网络架构 iOS开发 Windows
计算机网络实验【路由器的基本配置】
计算机网络实验【路由器的基本配置】
943 0
计算机网络实验【路由器的基本配置】
|
SQL 安全 数据库
故障解决:SQL Server数据库附加失败,错误3415、错误5120
本文为大家分享了SQL Server数据库附加失败的具体解决方法,供大家参考,具体内容如下
故障解决:SQL Server数据库附加失败,错误3415、错误5120
|
人工智能 中间件
呼叫中心中间件-网关配置
支持给网关指定变量,设置网关的语音编码编码和主叫号码。网关配置编辑后,不能实时生效 ,需要执行sofia命令才可以生效,具体看SIP设置。 配置 cti_gateway@domain [哈希表] key 网关名字 value 网关配置 | ``` { "param": { "register": "true", "caller-id-in-from": "true", "realm": "180.76.224.191:35560", "from-user": "", "destination-prefi
|
传感器 安全
嵌入式 STM32 SHT31温湿度传感器
嵌入式 STM32 SHT31温湿度传感器
|
移动开发 JavaScript 前端开发
面试题:渲染十万条数据解决方案
虚拟列表是最主流的解决方案,不渲染所有的数据,只渲染可视区域中的数据。
573 0
面试题:渲染十万条数据解决方案
|
开发框架 网络协议 安全
r0capture安卓应用层通杀脚本-使用文档
r0capture安卓应用层通杀脚本-使用文档
1091 1
r0capture安卓应用层通杀脚本-使用文档
|
Docker Windows 容器
Docker在win系统下上传文件到容器
在docker里面对应路径是C:/Users,docker默认的用户路径是/c/Users/windows的登录用户名,对应windows的登录用户目录:C:/Users/windows的登录用户名。
Docker在win系统下上传文件到容器