C# 确认格林斯潘';第十定律#
我正在尝试用C#实现一个基础设施,它允许我生成任意的数学表达式。例如,我希望能够接受这样的表达式 asin(sqrt(z-sin(x+y)^2)) 把它变成一个物体,让我可以用x,y,z来计算它,得到导数,还可能对它做一些符号代数。人们对C#中的好模式有什么看法 我有自己的观点,我担心这将进入建筑航天领域,所以我想确保情况并非如此 基本上,sin、+、sqrt等函数都有基于基类的类:C# 确认格林斯潘';第十定律#,c#,expression,currying,C#,Expression,Currying,我正在尝试用C#实现一个基础设施,它允许我生成任意的数学表达式。例如,我希望能够接受这样的表达式 asin(sqrt(z-sin(x+y)^2)) 把它变成一个物体,让我可以用x,y,z来计算它,得到导数,还可能对它做一些符号代数。人们对C#中的好模式有什么看法 我有自己的观点,我担心这将进入建筑航天领域,所以我想确保情况并非如此 基本上,sin、+、sqrt等函数都有基于基类的类: Function Function<TOut> : Function TOut Value
Function
Function<TOut> : Function
TOut Value
Function<Tin, TOut> : Function
TOut Evaluate(TIn value)
Function Derivative
Function<TOut, TIn> INverse
Function<TInA, TInB, TOut> : Function
TOut Evaluate(TInA valueA, TInB valueB)
Function PartialDerivativeA
Function PartialDerivativeB
函数
功能:功能
兜售价值
功能:功能
TOut评估(锡值)
函数导数
函数逆
功能:功能
TOut评估(TInA valueA、TInB valueB)
部分导数函数
函数PartialDerivativeB
到目前为止,一切都很简单。诀窍在于如何组合函数。在这里,我相信我想要一种类似于currying的方法,这样我就可以为单个参数计算函数,并保留其他参数。所以我在考虑这样的工厂课程:
Function<TInA, TInB, TOut> ->
Function<TInA, Function<TInB, TOut>>
(Function<TInA, TInB, TOut>, Function<TInX, TInA>, null) ->
Function<TInX, Function<TInB, TOut>>
(Function<TInA, TInB, TOut>, Function<TInA>, Function<TInX, TInY, TInB>) ->
Function<TInX, Function<TInY, TInB>>
函数->
作用
(函数,函数,null)->
作用
(功能,功能,功能)->
作用
等等。我主要担心的是泛型类型可能会使系统不可用(如果用户需要知道完整的泛型类型才能进行评估),并且我可能无法从输入参数构造所有泛型类型
谢谢你的意见 注意,可以使用C#编译器计算表达式 通过在运行时编译C#代码来计算数学表达式
我不完全确定什么是咖喱,但解析表达式的常用方法是构建一个。
从这一点上,应该不难计算表达式,找到导数,或者你想做的任何事情
[编辑]恐怕你的评论毫无意义。从它的发音来看,您想要解析一个表达式并构建一个AST,从中您可以对它做任何您想做的事情。是的,您将为每种类型的节点构建类;像这样的
public class PlusNode : BinaryNode
{
public PlusNode(Node left, Node right) { base(left, right); }
public virtual double Evaluate() { return Left.Evaluate() + Right.Evaluate(); }
public virtual Node BuildDerivative()
{
return new PlusNode(Left.BuildDerivative(), Right.BuildDerivative());
}
}
public class SinNode : UnaryNode
{
public SinNode(Node child) { base(child); }
public virtual double Evaluate() { return Math.Sin(Child.Evaluate()); }
public virtual Node BuildDerivative()
{
return new MultiplyNode(new CosNode(Child.Clone()), Child.BuildDerivative()); //chain rule
}
}
那使用什么呢?请注意,在链接页面上,甚至有一个构建某种curry函数的示例(从通用的“小于”运算符和固定常量构建“小于5”函数)有趣的是,我几个月前在D中确实做了这件事,但并没有得到特别的关注。我的方法是使用模板化的表达式树类。我有一个二进制类模板,可以用
+
,*
等来实例化,一元类可以用sin
,exp
等来实例化。派生类主要通过递归应用链和乘积规则来工作。例如:
class Binary(alias fun) : MathExpression {
MathExpression left, right;
MathExpression derivative() {
static if(is(fun == add)) {
return left.derivative + right.derivative;
} else static if(is(fun == mul)) {
return left.derivative * right + right.derivative * left;
}
}
real opCall(real x) {
return fun(left(x), right(x));
}
}
class Unary(alias fun) : MathExpression {
MathExpression inner;
MathExpression derivative() {
static if(is(fun == sin)) {
return Unary!(sin)(inner.derivative);
}
}
real opCall(real x) {
return fun(inner(x));
}
}
class Constant : MathExpression {
real val;
real opCall(real x) {
return val;
}
real derivative() {
return new Constant(0);
}
}
对我来说,编译后的表达式就像委托一样不透明。我需要对这些对象做一些元操作,比如显示底层表达式。Anders和他的团队打算打开C#编译器,允许它作为一种服务使用(这将提供更大的灵活性,因为当前的编译器本质上是一个黑匣子),但这种能力还不存在。对,问题是,基本上,如何表示AST的自由参数,灵活性和类型安全性之间的正确平衡是什么?@Reveazure:如果你说的“自由参数”是指“变量”,那么你可以随心所欲;可以存储字符串,也可以存储为具有名称和值的对象。这其实并不重要,只要你在树中找到一个,你就可以识别它(注意,所有的叶子都是数字或变量,反之亦然)。如果你对这个主题感兴趣的话,下面是我关于currying是什么的文章:到目前为止,所有回答的问题基本上是函数提供的功能,我需要类,而不是函数参数,我需要“curry”类类型参数。是的,但假设我有一个类似((/x(^y 2))的表达式。^是二进制运算符,但是(^y 2)只有一个参数。那么(^y 2)的类型应该是什么呢?我想你能看到我要去哪里。我坦率地承认我有点困惑。这就是为什么我要问。这里的问题是我真正想要的是一个类树,它有像ToString这样的东西,让我得到它们的导数。此外,我并不需要编译表达式或从C#代码动态生成表达式的能力。我建议使用Matlab或Mathematica之类的工具,而不是尝试重新发明轮子。为什么你认为你需要用泛型来实现这一点?使用普通的类层次结构似乎是完全可行的(而且稍微简单一点)。