2010-02-05 6 views
5

Estoy tratando de implementar una infraestructura en C# que me permita hacer expresiones matemáticas arbitrarias. Por ejemplo, quiero ser capaz de tomar una expresión comoConfirmando la 10ma ley de Greenspun en C#

asin (sqrt (z - sen (x + y)^2))

y convertirlo en un objeto que me permita evaluarlo en términos de x, y y z, obtenga derivados, y posiblemente haga algún tipo de álgebra simbólica en él. ¿Cuáles son las ideas de las personas sobre un buen modelo para esto en C#?

Tengo mi propia toma, que me temo que se dirige a la arquitectura astronáutica, así que quiero asegurarme de que no sea el caso.

Básicamente, las funciones como el pecado, +, sqrt, etc. tienen clases basado en una clase base:

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 

Hasta ahora, tan simple. El truco es cómo componer las funciones. Aquí creo que quiero algo así como un enfoque currying para que pueda evaluar la función de un solo parámetro, y tener los otros permanecen. Así que estoy pensando en tener una clase de fábrica como esta:

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>> 

y así sucesivamente. Mi principal preocupación es que los tipos genéricos pueden inutilizar el sistema (si se requiere que el usuario conozca los tipos genéricos completos solo para evaluar), y que quizás no pueda construir todos los tipos genéricos a partir de los argumentos de entrada.

¡Gracias por su contribución!

+0

Sugiero usar algo como Matlab o Mathematica o lo que sea, en lugar de tratar de reinventar la rueda. – davr

+0

¿Por qué crees que debes hacer esto con genéricos? Parece perfectamente factible (y un poco más simple) usando una jerarquía de clases normal. – RBarryYoung

Respuesta

1

Tenga en cuenta que es posible usar el compilador C# para evaluar expresiones.

Evaluación de expresiones matemáticas mediante la compilación de código C# en tiempo de ejecución http://www.codeproject.com/KB/recipes/matheval.aspx

+0

Parece que la expresión compilada sería tan opaca para mí como lo haría un delegado. . . Voy a necesitar hacer meta-cosas para estos objetos, como mostrar la expresión subyacente. – reveazure

+0

Cierto. Anders y su equipo intentan abrir el compilador de C#, permitiendo que se use como un servicio (lo que proporcionaría una flexibilidad mucho mayor, ya que el compilador actual es esencialmente una caja negra), pero esa capacidad aún no existe. –

0

No estoy del todo seguro de lo que es currificación, pero el enfoque habitual de expresiones de análisis sintáctico es construir una abstract syntax tree.
A partir de esto no debería ser difícil evaluar la expresión, encontrar la derivada o lo que sea que quiera hacer.


[Editar] me temo que sus comentarios no tienen sentido. A partir de sus sonidos, quiere analizar una expresión y crear un AST, desde el que puede hacer lo que quiera con él. Sí, crearás clases para cada tipo de nodo; algo como esto

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 
    } 
} 
+0

Correcto, la pregunta es, básicamente, cómo representar los parámetros libres de AST, y también ¿cuál es el equilibrio correcto entre flexibilidad y seguridad de tipo? – reveazure

+0

@Reveazure: si por "parámetros libres" quiere decir "variables", hágalo como lo desee; podría almacenar cadenas, o como objetos con un nombre y un valor. Realmente no importa, siempre y cuando llegue a uno en el árbol puede reconocerlo (tenga en cuenta que todas las hojas serán números o variables, y viceversa) –

+0

Aquí está mi artículo sobre lo que es currying, si están interesados ​​en ese tema: http://blogs.msdn.com/ericlippert/archive/2009/06/25/mmm-curry.aspx –

0

¿Qué pasa con el uso de Expression Trees? Tenga en cuenta que en la página enlazada, hay incluso un ejemplo para construir una especie de función curried (construyendo una función "menos de cinco" de un operador genérico "menor que" y una constante fija)

+0

El problema aquí es que lo que realmente quiero es un árbol de clases, que tengan cosas como ToString y déjenme obtener sus derivados. Además, realmente no necesito la capacidad de compilar la expresión o generarla dinámicamente desde el código C#. – reveazure

0

Gracioso, de hecho lo hice hace unos meses en D y no fue recibido como particularmente interesante. Mi enfoque fue utilizar clases de árbol de expresiones con plantillas. Tenía una plantilla de clase binaria que podía crearse una instancia con +, *, etc., una clase única que podía crearse una instancia con sin, exp, etc. Los derivados funcionaban principalmente aplicando recursivamente las reglas de cadena y producto.Por ejemplo:

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); 
    } 
} 
Cuestiones relacionadas