当前位置: 首页 > news >正文

纯 C# 中使用 FParsec 的高级组合器示例代码

以下是纯 C#中使用 FParsec 的高级组合器示例代码。由于 FParsec 本身是 F# 库,直接在 C# 中使用时语法会比较繁琐(需要大量>>.>>%|>等操作符)。

推荐两种方式:

  1. 最简单推荐:直接使用MathNet.Symbolics内置的 Infix 解析器(它内部已经封装了 FParsec 的强大功能)。
  2. 原生 FParsec:安装FParsec.CSharp(一个薄的 C# 友好包装器),语法更接近 F#。

方式一:使用 MathNet.Symbolics.Infix(最推荐,零 FParsec 代码)

usingSystem;usingMathNet.Symbolics;usingExpr=MathNet.Symbolics.SymbolicExpression;classProgram{staticvoidMain(){// 1. 基本解析(支持 + - * / ^ 函数 括号 优先级)varexpr1=Infix.ParseOrThrow("2 + 3 * 4^2");Console.WriteLine(expr1);// 输出: 2 + 3*4^2Console.WriteLine(expr1.Expand());// 输出: 50varexpr2=Infix.ParseOrThrow("(2 + 3) * 4^2");Console.WriteLine(expr2.Evaluate());// 输出数值结果// 2. 带变量varx=Expr.Variable("x");vary=Expr.Variable("y");varexpr3=Infix.ParseOrThrow("x^2 + 3*x + sin(pi*y)");Console.WriteLine(expr3);// 3. 求值(提供变量值)varsymbols=newDictionary<string,FloatingPoint>{{"x",2.0},{"y",0.5}};Console.WriteLine(expr3.Evaluate(symbols).RealValue);// 4. 错误处理try{Infix.ParseOrThrow("2 + * 3");// 会抛异常}catch(Exceptionex){Console.WriteLine("解析失败: "+ex.Message);}// 5. ParseOrUndefined(不抛异常,返回 Undefined)varbad=Infix.ParseOrUndefined("2 + * 3");Console.WriteLine(bad);// 输出: Undefined}}

优点:无需手动写任何组合器,MathNet.Symbolics 已经用 FParsec 实现了完整的数学表达式解析器,支持优先级、结合性、函数等。

方式二:使用 FParsec.CSharp 包装器(更接近原生 FParsec 高级组合器)

先通过 NuGet 安装两个包:

Install-Package FParsec Install-Package FParsec.CSharp
usingSystem;usingFParsec.CSharp;// 使用 C# 包装器usingFParsec;// 原始 FParsec 类型usingstaticFParsec.CSharp.CharParsers;usingstaticFParsec.CSharp.Primitives;usingstaticFParsec.CSharp.OperatorPrecedenceParser;classProgram{staticvoidMain(){// 示例1:逗号分隔数字列表(sepBy)varpNumber=Pint32.AndSkip(Spaces);varpList=SepBy(pNumber,CharP(',').AndSkip(Spaces));varresult1=pList.Run("1, 2, 3, 42");Console.WriteLine(result1.Result);// 输出: [1, 2, 3, 42]// 示例2:括号内的表达式(between)varpInBrackets=Between(CharP('(').AndSkip(Spaces),Spaces.AndSkip(CharP(')')),pNumber);varresult2=pInBrackets.Run("( 123 )");Console.WriteLine(result2.Result);// 输出: 123// 示例3:简单加减法(chainl1)varpTerm=Pint32;varpAddOp=CharP('+').Return<Func<int,int,int>>((x,y)=>x+y).Or(CharP('-').Return<Func<int,int,int>>((x,y)=>x-y));varpExpr=Chainl1(pTerm,pAddOp.AndSkip(Spaces));Console.WriteLine(pExpr.Run("10 + 20 - 5").Result);// 输出: 25// 示例4:完整带优先级的表达式计算器(OperatorPrecedenceParser)—— 最强大varopp=newOperatorPrecedenceParser<double,Unit,Unit>();varexprParser=opp.ExpressionParser;// 添加运算符(优先级从低到高)opp.AddOperator(Infix("+",Assoc.Left,10,(x,y)=>x+y));opp.AddOperator(Infix("-",Assoc.Left,10,(x,y)=>x-y));opp.AddOperator(Infix("*",Assoc.Left,20,(x,y)=>x*y));opp.AddOperator(Infix("/",Assoc.Left,20,(x,y)=>x/y));opp.AddOperator(Infix("^",Assoc.Right,30,Math.Pow));// 基本项:数字 或 (表达式)varpNumberDouble=PFloat.AndSkip(Spaces);varterm=pNumberDouble.Or(Between(CharP('(').AndSkip(Spaces),Spaces.AndSkip(CharP(')')),exprParser));opp.TermParser=term;// 测试Console.WriteLine(exprParser.Run("2 + 3 * 4^2").Result);// 输出: 50Console.WriteLine(exprParser.Run("2^3^2").Result);// 输出: 512(右结合)Console.WriteLine(exprParser.Run("(2 + 3) * 4^2").Result);// 输出: 80}}

总结对比

  • 想快速解析数学表达式→ 直接用MathNet.Symbolics.Infix.ParseOrThrow/ParseOrUndefined(最简单)。
  • 想自己构建任意复杂语法(JSON、DSL、自定义语言)→ 使用FParsec.CSharp,代码更清晰。
  • 不想引入额外包装器→ 可以直接调用 FParsec 的原始 API(但代码会更长,需要>>.>>%等 F# 风格操作符)。

如果你需要:

  • 解析带变量的完整表达式求值器
  • JSON 解析示例(C# 版)
  • CSV / 自定义 DSL
  • 错误信息美化处理

告诉我具体需求,我可以立刻给你更完整的代码!
直接复制上面代码到 Console 项目,安装对应 NuGet 包即可运行。

以下是MathNet.Symbolics(最新版 0.25.0)的完整 C# 示例,涵盖从基础到高级的常用功能。

1. 项目准备(NuGet 安装)

Install-Package MathNet.Symbolics-Version0.25.0# 或者dotnetaddpackage MathNet.Symbolics--version0.25.0

2. 完整控制台示例(推荐直接复制运行)

usingSystem;usingSystem.Collections.Generic;usingMathNet.Symbolics;usingExpr=MathNet.Symbolics.SymbolicExpression;// 常用别名,简化代码classProgram{staticvoidMain(string[]args){Console.WriteLine("=== MathNet.Symbolics 完整演示 ===\n");// ==================== 1. 定义变量 ====================varx=Expr.Variable("x");vary=Expr.Variable("y");vara=Expr.Variable("a");varb=Expr.Variable("b");Console.WriteLine("变量定义完成:x, y, a, b");// ==================== 2. 构建表达式(运算符重载) ====================varexpr1=a+a;// 自动化简为 2*avarexpr2=a*a;// a^2varexpr3=2+1/x-1;// 1 + 1/xvarexpr4=(a/b/(x*a))*(x*b/a)/b;Console.WriteLine($"a + a ={expr1}");Console.WriteLine($"a * a ={expr2}");Console.WriteLine($"2 + 1/x - 1 ={expr3}");Console.WriteLine($"复杂分式化简 ={expr4}");// 输出: 1/(a*b)// ==================== 3. 字符串解析(Infix) ====================varexpr5=Infix.ParseOrThrow("x^2 + 3*x + 2");// 抛异常(推荐用于严格检查)varexpr6=Infix.ParseOrUndefined("sin(pi*x) + cos(2*x)");// 不抛异常,失败返回 UndefinedConsole.WriteLine($"\n解析字符串:{expr5}");Console.WriteLine($"解析含函数:{expr6}");// ==================== 4. 输出格式 ====================Console.WriteLine($"\nLaTeX 输出:{expr5.ToLaTeX()}");// \frac{...}Console.WriteLine($"内部结构:{expr5.ToInternalString()}");// ==================== 5. 代数操作(展开、化简、因式分解) ====================varpoly=(x+1).Pow(3);// (x+1)^3Console.WriteLine($"\n(x+1)^3 展开前:{poly}");Console.WriteLine($"展开后:{poly.Expand()}");// x^3 + 3*x^2 + 3*x + 1vartrig=x.Cos().Pow(4);Console.WriteLine($"三角化简:{trig.TrigonometricContract()}");// ==================== 6. 求导(Differentiate) ====================varderiv1=expr5.Differentiate(x);// 对 x 求导Console.WriteLine($"\n对 x^2 + 3x + 2 求导:{deriv1}");// 2*x + 3varderiv2=x.Sin().Differentiate(x);Console.WriteLine($"sin(x) 的导数:{deriv2}");// cos(x)// ==================== 7. 数值求值(Evaluate) ====================varsymbols=newDictionary<string,FloatingPoint>{{"x",2.0},{"y",3.0}};varvalue1=(x*x+y).Evaluate(symbols);Console.WriteLine($"\n当 x=2, y=3 时,x^2 + y ={value1.RealValue}");// 7.0// ==================== 8. 编译为委托(高性能重复求值) ====================Func<double,double,double>f=(x*x+y).Compile("x","y");Console.WriteLine($"编译后求值 f(5, 4) ={f(5,4)}");// 29// ==================== 9. 替换变量(Substitute) ====================varsubstituted=(x*x+3*x).Substitute(x,Expr.FromInt32(5));Console.WriteLine($"把 x 替换为 5:{substituted}={substituted.Evaluate().RealValue}");// ==================== 10. Taylor 展开示例(自定义函数) ====================ExprTaylor(intorder,Exprsymbol,Exprat,Exprfunc){intfactorial=1;Expraccumulator=Expr.Zero;Exprderivative=func;for(inti=0;i<order;i++){varsubs=derivative.Substitute(symbol,at);derivative=derivative.Differentiate(symbol);accumulator=accumulator+subs/factorial*(symbol-at).Pow(i);factorial*=(i+1);}returnaccumulator.Expand();}vartaylorSinCos=Taylor(5,x,Expr.Zero,x.Sin()+x.Cos());Console.WriteLine($"\nsin(x) + cos(x) 在 x=0 处的 Taylor 展开(5 阶):\n{taylorSinCos}");Console.WriteLine("\n=== 演示结束 ===");Console.ReadKey();}}

常用功能速查表

功能代码示例说明
创建变量Expr.Variable("x")定义符号变量
解析字符串Infix.ParseOrThrow("x^2 + 3*x")严格解析,推荐
自动/手动化简expr.Expand()/expr.Simplify()展开、多项式化简
三角化简expr.TrigonometricContract()sin/cos 恒等式化简
求导expr.Differentiate(x)对指定变量求导
数值求值expr.Evaluate(symbols)代入数值字典
编译为函数expr.Compile("x", "y")高性能重复计算
LaTeX 输出expr.ToLaTeX()生成 LaTeX 公式
替换变量expr.Substitute(old, new)变量替换

注意事项

  • 自动化简:MathNet.Symbolics 在构建表达式时会尽量自动化简(如a + a2*a),你不需要手动调用Simplify()很多时候。
  • ParseOrThrow vs ParseOrUndefined:前者抛异常(适合用户输入验证),后者返回Undefined(更安全)。
  • 依赖:会自动拉取MathNet.NumericsFSharp.CoreFParsec等,无需手动安装。
  • 性能:大量重复求值时,优先使用.Compile()生成委托。

把上面的代码粘贴到一个新的Console App项目中,安装 NuGet 包后直接运行即可看到完整输出。

如果你还需要以下特定示例,请告诉我,我立刻补充:

  • 方程求解(Solve)
  • 积分(Integrate)
  • 矩阵/向量符号运算
  • 与 MathNet.Numerics 数值库结合
  • 生成 LaTeX 并渲染

这个示例已经覆盖了日常使用中 90% 以上的场景,祝你使用愉快!

http://www.cnnetsun.cn/news/1459096.html

相关文章:

  • 别再被MOS管炸机搞懵了!手把手教你分析米勒平台波形(附实测图)
  • Go项目实战:用Swagger自动生成API文档,告别手写接口说明的烦恼
  • MOS管与三极管的驱动特性对比及选型指南
  • XYC-ALS21C-K1环境光传感器驱动开发与低功耗嵌入式实践
  • 【63页PPT】数字乡村智慧农业顶层设计方案:顶层规划设计、农业大数据、物联网、党建信息化、电商平台、质量追溯、智慧旅游
  • 2025年IDM激活终极指南:简单三步实现永久免费使用
  • ESP32 C3 vs S3开发板功耗实测:如何为你的IoT项目选择更省电的方案?
  • 优化Docker镜像拉取:解决pull错误与加速下载的实用指南
  • 3步定制专属键位方案:QKeyMapper让Win10/11按键配置更高效
  • SI1145传感器寄存器级驱动与低功耗设计详解
  • 不用U盘!Win11硬盘直装保姆教程(含BitLocker关闭与分区避坑指南)
  • 给 SAP ABAP CDS View 的 OData 元数据起一个好名字:把 EntityType 与 EntitySet 设计成真正可用的 API 契约
  • Qwen3-0.6B-FP8 FP8量化效果展示:显存仅2GB的惊艳推理表现
  • 传统传感器数据直接采集,程序加入动态滤波算法,剔除随机干扰,测数比传统准30%
  • Unity3D WEBGL项目实战:如何解决数据库连接与字体显示问题(附代码示例)
  • Notepad--:重新定义跨平台文本编辑器的国产技术解决方案
  • 做电商利润上不去?用对Ta每月多赚2W真不难
  • PP-DocLayoutV3代码实例:批量处理图像目录并生成结构化JSON报告
  • RePKG技术指南:Wallpaper Engine资源处理全解析
  • mmsegmentation 自定义模型注册失败:深入解析 ‘EncoderDecoder‘ 的注册机制与修复实践
  • 告别cURL!用libhv的HttpMessage类手把手教你构建更灵活的HTTP请求(附JSON/FormData实战)
  • 告别纯GPS:手把手教你为Pixhawk无人车配置视觉惯性导航(VIO)与MAVROS融合定位
  • 零代码实战:2小时用织信Informat搭建企业级出入库系统(附完整配置截图)
  • 图片旋转判断模型在数字档案馆中的应用:历史文献扫描图自动校正
  • 虚拟机练习
  • 微信小程序视频封面获取实战:从wx.chooseVideo到wx.chooseMedia的升级方案
  • 隐私计算实践:OpenClaw+nanobot镜像本地化知识问答
  • 【架构心法】撕碎虚函数表的伪善!在盾构机采集板上拒绝动态绑定,用 C++ CRTP 黑魔法构筑“零开销”静态多态
  • PaddleOCR手写体识别实战:从数据标注到模型微调的全流程避坑指南
  • 数学推导可视化:用Python动态演示雷诺运输定理的物理意义