Подтверждение 10-го закона Гринспуна в C # - PullRequest
5 голосов
/ 05 февраля 2010

Я пытаюсь реализовать инфраструктуру в C #, которая позволила бы мне создавать произвольные математические выражения. Например, я хочу иметь возможность взять выражение вроде

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

и превратить его в объект, который позволит мне оценить его с точки зрения x, y и z, получить производные и, возможно, выполнить некоторую символическую алгебру над ним. Что думают люди о хорошей модели для этого в C #?

У меня есть своя собственная идея, которая, боюсь, направляется в архитектурную космонавтику, поэтому я хочу убедиться, что это не так.

По существу, функции типа sin, +, sqrt и т. Д. Имеют классы, основанные на базовом классе:

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

Пока все просто. Хитрость в том, как составлять функции. Здесь я считаю, что мне нужно что-то вроде подхода с каррированием, чтобы я мог оценить функцию для одного параметра и оставить остальные. Так что я думаю о заводском классе, подобном этому:

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

и так далее. Мои главные опасения заключаются в том, что универсальные типы могут сделать систему непригодной для использования (если пользователь должен знать полные универсальные типы только для оценки), и что я не смогу создать все универсальные типы из входных аргументов. 1015 *

Спасибо за ваш вклад!

Ответы [ 4 ]

1 голос
/ 05 февраля 2010

Обратите внимание, что для оценки выражений можно использовать компилятор C #.

Оценка математических выражений путем компиляции кода C # во время выполнения http://www.codeproject.com/KB/recipes/matheval.aspx

0 голосов
/ 06 февраля 2010

Забавно, я действительно делал это несколько месяцев назад в 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);
    }
}
0 голосов
/ 05 февраля 2010

Как насчет использования деревьев выражений ? Обратите внимание, что на связанной странице есть даже пример для создания своего рода функции с карри (создание функции «меньше пяти» из универсального оператора «меньше чем» и фиксированной константы)

0 голосов
/ 05 февраля 2010

Я не совсем уверен, что такое карринг, но обычный подход к синтаксическому анализу выражений состоит в создании абстрактного синтаксического дерева .
Отсюда нетрудно оценить выражение, найти производную или что-то еще, что вы хотите сделать.


[Редактировать] Боюсь, ваши комментарии не имеют смысла. Судя по его звукам, вы хотите разобрать выражение и построить 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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...