使用自定义脚本函数评估数学表达式
Evaluating mathematical expressions with custom script functions
我正在寻找一种算法或方法来评估表示为字符串的数学表达式。该表达式包含数学组件,但也包含自定义函数。我希望在 C#/.Net 中实现上述算法。
我知道 Roslyn 允许我计算这种表达式
"var value = 3+5*11-Math.Sqrt(9);"
我也很熟悉如何使用 "node re-writing" 来避免变量声明或完全限定的函数名称或省略尾随分号以评估
"value = 3+5*11-Sqrt(9)"
然而,我想在此之上实现的是提供自定义脚本功能,例如
"value = Ratio(A,B)"
,其中 Ratio 是一个自定义函数,它将向量 A 中的每个元素除以向量 B 中的每个元素,returns 是一个相同长度的向量。
或
"value = Sma(A, 10)"
,其中 Sma 是一个自定义函数,用于计算 vector/timeseries A 的简单移动平均线,回顾 window 为 10.
理想情况下,我希望能够提供更复杂的功能,例如
"value = Ratio(A,B) * Pi + 0.5 * Spread(C,D) + Sma(E, lookback)"
,由此解析引擎将尊重运算符优先级并构建解析树以获取评估表达式所需的值。
我不知道如何用 Roslyn 解决此类问题。
还有哪些其他方法可以帮助我入门,或者我是否缺少 Roslyn 提供的可能有助于解决此问题的功能?
假设您的所有表达式都是有效的 C# 表达式,您可以通过多种方式使用 Roslyn。
您只能将 Roslyn 用于解析。 SyntaxFactory.ParseExpression
会给你一个表达式的语法树。请注意,您的第一个 (var v = expr;
) 示例不是表达式,而是变量声明。但是 v = expr
是一个表达式,即 AssignmentExpressionSyntax
。然后你可以遍历这个 AST,对每个节点做你想做的事,基本上你会写一个解释器。这种方法的好处是你不必编写自己的解析器,遍历 AST 非常简单,而且这种方法很灵活,因为定义你用 "unknown" 方法做什么完全取决于你.
也使用 Roslyn 进行评估。这可以通过多种方式完成:将一个有效的 C# 文件放在一起,然后将其编译成一个程序集,或者您可以通过 Scripting API。这种方法基本上需要一个 class 库,其中包含所有额外方法的实现,例如 Sma
、Spread
、...方法,所以这并不是一个额外的努力。
如果唯一的目标是计算表达式,那么我会选择第二种方法。如果有额外的要求(你没有提到)比如能够生成一个简化的表达式,那么我会考虑第一个解决方案。
- 如果您找到一个完全满足您需要的库(并且性能很好,并且您不介意对第 3 方工具的依赖,...),我会选择它。
MathParser.org-mXparser
评论中的建议似乎正是您要找的。
我正在寻找一种算法或方法来评估表示为字符串的数学表达式。该表达式包含数学组件,但也包含自定义函数。我希望在 C#/.Net 中实现上述算法。
我知道 Roslyn 允许我计算这种表达式
"var value = 3+5*11-Math.Sqrt(9);"
我也很熟悉如何使用 "node re-writing" 来避免变量声明或完全限定的函数名称或省略尾随分号以评估
"value = 3+5*11-Sqrt(9)"
然而,我想在此之上实现的是提供自定义脚本功能,例如
"value = Ratio(A,B)"
,其中 Ratio 是一个自定义函数,它将向量 A 中的每个元素除以向量 B 中的每个元素,returns 是一个相同长度的向量。
或
"value = Sma(A, 10)"
,其中 Sma 是一个自定义函数,用于计算 vector/timeseries A 的简单移动平均线,回顾 window 为 10.
理想情况下,我希望能够提供更复杂的功能,例如
"value = Ratio(A,B) * Pi + 0.5 * Spread(C,D) + Sma(E, lookback)"
,由此解析引擎将尊重运算符优先级并构建解析树以获取评估表达式所需的值。
我不知道如何用 Roslyn 解决此类问题。
还有哪些其他方法可以帮助我入门,或者我是否缺少 Roslyn 提供的可能有助于解决此问题的功能?
假设您的所有表达式都是有效的 C# 表达式,您可以通过多种方式使用 Roslyn。
您只能将 Roslyn 用于解析。
SyntaxFactory.ParseExpression
会给你一个表达式的语法树。请注意,您的第一个 (var v = expr;
) 示例不是表达式,而是变量声明。但是v = expr
是一个表达式,即AssignmentExpressionSyntax
。然后你可以遍历这个 AST,对每个节点做你想做的事,基本上你会写一个解释器。这种方法的好处是你不必编写自己的解析器,遍历 AST 非常简单,而且这种方法很灵活,因为定义你用 "unknown" 方法做什么完全取决于你.也使用 Roslyn 进行评估。这可以通过多种方式完成:将一个有效的 C# 文件放在一起,然后将其编译成一个程序集,或者您可以通过 Scripting API。这种方法基本上需要一个 class 库,其中包含所有额外方法的实现,例如
Sma
、Spread
、...方法,所以这并不是一个额外的努力。
如果唯一的目标是计算表达式,那么我会选择第二种方法。如果有额外的要求(你没有提到)比如能够生成一个简化的表达式,那么我会考虑第一个解决方案。
- 如果您找到一个完全满足您需要的库(并且性能很好,并且您不介意对第 3 方工具的依赖,...),我会选择它。
MathParser.org-mXparser
评论中的建议似乎正是您要找的。