Вычисление выражений на Nemerle и Mono.

За weekend на хабре появились три статьи по разбору математических выражений: Компилятор выражений, Парсер математических выражений и Вычисление значения выражения и один комментарий, в котором код на прологе по краткости и выразительности рвет примеры в этих статьях.

Ниже я покажу, как использовать стандартные средства платформы и языка для достижения того же результата, а именно вычислению выражений.

На конференции PDC2008 Anders Hejlsberg сделал доклад, в котором проследил эволюцию языка C#. Он рассмотрел уже существующие версии и сделал обзор будущих: в 4ой версии разработчиков ждет возможность разбавить строгую систему типов возможностями из динамических языков, а к пятой версии компилятор будет полностью переписан под управляемый код, и его можно будет использовать как сервис (compiler as a service).

Для тех программистов, которые мечтают опробовать эту идеологию (compiler as a service) уже сейчас, будет приятной новостью то, что они могут это сделать. Дело в том, что компилятор nemerle написан на nemerle, и его можно использовать как простую библиотеку. Стоит так же отметить то, что в этом случае в процессе его работы не будет создано на диске никаких файлов, только если Вы не хотите обратного. Ниже — пример кода, который использует компилятор как библиотеку и который эффективно решает проблему разбора/вычислений выражений=)

using System;

using System.Console;
using Nemerle.Evaluation.Evaluator;

module Program
{
  Main() : void
  {
    def function = EvaluateExpression("x => x + 1.0") :> double->double;
    WriteLine(function(2.0));
  }
}


Думаю, что любому программисту, владеющим C# этот код понятен, отдельно отмечу только, что module — синоним static class, double->double — тип функции, аналог Func<double,double>, а using System.Console раскрывает System.Console так, что его статические методы «превращаются» в глобальные функции/процедуры, то есть вызов WriteLine(function(2.0)) это на самом деле вызов Console.WriteLine(function(2.0)).

Другой возможностью попробовать будущее на вкус является платформа mono. Я долго ждал выхода версии 2.2 и удивлен, что на хабре про её выход еще никто не написал. Мои ожидания были основаны на заметке, которую Miguel De Icaza опубликовал в своем блоге в сентябре, в нем он анонсировал, что в следующей версии mono (теперь она уже текущая) добавиться REPL, который в свою очередь основан на идеологии compiler as service. Ниже — код на C# под mono, аналогичный коду на Nemerle:

using System;
using Mono.CSharp;

public class Program
{
  public static void Main(string[] args)
  {
    var function =
      Evaluator.Evaluate ("new System.Converter<double,double>(x=>x+1.0);")
      as Converter<double,double>;
    Console.WriteLine(function(2.0));
  }
}


P.S.
Отдельно хочется отметить, то что и Nemerle, и Mono являются open source проектами. Эту особенность я использовал при создании этой заметки, а именно, исчерпывающей документации по использования compile as a service в Nemerle и Mono я не нашел, но зато быстро смог найти всю интересующую меня информацию, изучая исходные коды.
+19
26 января 2009, 20:32
7
shai_xylyd 38,0

комментарии (15)

+1
veitmen #
Ох как красиво! Ну все, с выходных ищу время для «вкуривания» MONO.
Или все таки ждать C#4.0 — 5.0?
0
shai_xylyd #
Если целевая платформа у вас .net, то имеет смысл использовать nemerle. А у моно сейчас есть проблемы, например, в сборке под windows nunit 2.4.8 падает с ошибками.
0
veitmen #
Спасибо, приму к сведению. Просто у nemerle меня как то синтаксис смутил… :-(
+3
alex_blank #
там хороший синтаксис, в традициях ФП :)

а то всякие форки C (C++, Java) к дурному приучили…
+2
pieceofsummer #
А в шарпе это DynamicExpression.ParseLambda() :)
0
Thecentury #
Что-то я не нашел такого. Это откуда? Какая версия C#? Какая сборка?
Или же это пока еще мечты? )
+2
pieceofsummer #
+2
gaploid #
привет javascript, flash и expression injection=)
0
darkk #
Присоединяюсь к вопросу — создается впечатление, будто бы eval в C# не такое уж и абсолютное зло.
Мне кажется, если безопасность/надежность какая-либо нужна, то лучше всё-таки хотя бы проконтролировать формальной грамматикой выражение перед eval-ом (тогда не надо писать полноценный «интерпретатор» выражения).
0
shai_xylyd #
Это точно, кроме того вы сами контролируете сборки, к которым имеет доступ этот опасный код, я еще до конца не разобрался, но думаю, что можно ограничить классы, к которым имеет доступ этот код, а если нельзя, то скорее всего в будущих версиях это ограничение появится.
0
pieceofsummer #
По-хорошему, динамический код вообще можно грузить в отдельный AppDomain с ограниченным доступом куда-либо.
+3
xonix #
ну это не интересно, так и на питоне нетрудно

>>> add5 = eval('lambda a: a+5')
>>> add5(2)
7

то же самое на руби, джаваскрипте, смолтолке,… (подставьте свой любимый язык).

Проблема такого подхода — мы завязаны на синтаксисе конкретного языка => не гибко
0
xonix #
впрочем, часто (например, построить график по введенной формуле) может оказаться достаточно )
п.с. спасибо за отзыв на мой пост! )
0
elw00d #
Спасибо за интересный способ, позволяет несколько по-новому взглянуть на наш инструментарий ) Однако классические алгоритмы и умение их применять никто не отменял ) Тем более, если своя реализация может обеспечить более гибкий механизм для контроля каждой стадии анализа / вычисления. К примеру, в собственном парсере-велосипеде можно рулить как выражением в целом, так и отдельными его частями (например, оптимизировать выражение за счет предвычисления констант). В общем, проблема выбора между своей собственной разработкой и использованием готовых решений как всегда актуальна.
0
shai_xylyd #
Это точно, но на некотором классе задач такой подход будет предпочтительней, например, на лабах по численным методам, при генерации кода на лету, etc…

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.