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)

相关文章
|
JavaScript Windows 容器
Flutter Web:根据浏览器窗口改变布局大小
之前我们通过flutter开发web应用,然后用electron打包成可执行文件在pc端使用,因为electron可以设置最小宽高,所以布局不会越界,但是如果直接在浏览器中打开,因为浏览器的大小无法控制,如果用户缩小浏览器会导致布局越界。根据大部分网站的经验来看,当窗口缩小到一定程度后,布局就不会再改变,反而是增加了滚动,这样就保证了布局的正确性,所以我们也打算这么做。
1881 0
|
5月前
|
人工智能 自然语言处理 机器人
零基础保姆级图文教程|阿里云计算巢部署OpenClaw(Clawdbot) 钉钉集成+大模型配置全流程
2026年,AI自动化框架OpenClaw(原Clawdbot)凭借阿里云计算巢一键部署、云端稳定运行与钉钉深度集成能力,成为企业与个人打造AI办公助手的核心工具。计算巢提供官方认证的OpenClaw部署模板,无需手动配置环境,5分钟即可完成云端部署;搭配阿里云千问大模型、免费Coding Plan API,再通过钉钉渠道实现群聊/私聊指令交互、任务自动执行、结果实时推送,满足办公效率提升、团队协作、轻量业务自动化等场景需求。
1135 5
|
11月前
|
安全 5G 网络安全
SD-WAN技术概述:软件定义广域网的工作原理
总结起来,Sd-wan作为一项创新技术,在现代快速变换且日益复村多样环境下提供了一个灵活高效且具有经济效益解决方案。随着数字转型趋势持续推进,Sd-wan无疑会在未来扮演更重要角色,在助力企业建立更智慧强骨干同时也促进整体行业向前发展步伐。
611 17
|
11月前
|
人工智能 运维 中间件
CTI中间件:现代呼叫中心的核心开发平台
乐科CTI中间件助力企业高效构建智能呼叫中心,集成通信与业务系统,支持多协议、AI融合、高可用部署,广泛应用于客服、营销等场景,降低开发成本,提升运维效率。
|
11月前
|
存储 机器学习/深度学习 算法
【路径规划】3D 网格地图上规划路径(Matlab代码实现)
【路径规划】3D 网格地图上规划路径(Matlab代码实现)
728 5
|
网络协议 C++ 网络架构
【革命性升级】OSPFv3 vs OSPFv2:揭秘IPv6时代下的网络协议革新之旅!
【8月更文挑战第22天】OSPFv2与OSPFv3分别是用于IPv4和IPv6网络的路由协议。OSPFv2绑定于32位IPv4地址,而OSPFv3支持128位IPv6地址并具备地址独立性,LSA不再包含具体IPv6地址。OSPFv3引入多实例能力,可在同一接口上运行多个实例,支持多种IPv6地址族。邻居发现机制方面,OSPFv3使用NDP而非ARP。此外,OSPFv3支持更细粒度的路由聚合。这些改进使OSPFv3更适应当今网络需求。
651 1
|
前端开发 安全 Java
java发布公告的实现流程
构建一个Java公告发布系统涉及到前端界面设计、后端业务逻辑处理、数据库设计与交互、安全性保障等多个环节。通过采用现代的开发框架和最佳实践,可以高效地开发出既安全又易于维护的系统。随着需求的增长,系统还可以进一步扩展,比如增加评论功能、通知订阅、多语言支持等。
533 1
|
Kubernetes 负载均衡 Cloud Native
从零开始入门 K8s | 理解 CNI 和 CNI 插件
网络架构是 K8s 中较为复杂的方面之一。K8s 网络模型本身对某些特定的网络功能有着一定的要求,因此,业界已经有了不少的网络方案来满足特定的环境和要求。CNI 意为容器网络的 API 接口,为了让用户在容器创建或销毁时都能够更容易地配置容器网络。在本文中,作者将带领大家理解典型网络插件地工作原理、掌握 CNI 插件的使用。
从零开始入门 K8s | 理解 CNI 和 CNI 插件
|
前端开发 小程序 JavaScript