Денис писал(а):Чтобы рассчитать результат функции, нужен класс, который называется «парсер математических выражений». Таких очень много у нас в разделе «кирпичный завод» и даже для дотнета есть.
Насколько я понял, все они ориентированы на обычную (инфиксную) нотацию. Куда проще работать с т. н. «польской» (префиксной) записью, наиболее известной по языку Лисп.
Например, вместо
- Код: Выделить всё
Abs(x ^ 3) - 12 * Sin(x / 10)
будет
- Код: Выделить всё
(- (Abs (^ x 3)) (* 12 (Sin (/ x 10))) )
Очевидное преимущество такого формата входных данных —
очень лёгкое написание парсера и ещё более лёгкое расширение своего языка. Не нужны никакие регулярные выражения, не нужно составлять никакие деревья обхода операндов, учитывать приоритет операторов. (Кстати, заметьте: здесь у любого оператора может иметься произвольное количество аргументов.) От языка требуется только поддержка рекурсии.
Если нужно только считать математические формулы, то парсер можно написать даже на таком ограниченном (для подобных вещей) языке, как досовский QuickBasic. Если же в наших руках вся мощь .NET с его variant-типами данных и поддержкой списков и словарей, то несложно будет создать целый язык программирования, чтобы позволить пользователю применять более сложные конструкции, например:
- Код: Выделить всё
(defun MyFunction (t)
(if (< t 0) 0 (^ t 1.5))
)
(+ 1 x (MyFunction (- 3 x)) )
То есть создавать ветвления, циклы, переменные и функции.
Кстати, вопреки распространённому синтаксису, продемонстрированному в этом примере, мне кажется, проще поступать немного иначе:- Код: Выделить всё
(defun 'MyFunction ('t) ...)
Отличие здесь — в апострофе перед именем «символа», который указывает, что символ надо воспринимать как сам символ (по сути, как ссылку на объект), а не как значение этого символа. Такой подход упрощает создание парсера, позволяя унифицировать схему вычислений, вместо того чтобы требовать какого-то особого отношения к элементам подобных синтаксических конструкций.Очевидный недостаток префиксной нотации — сложность для написания и восприятия, даже у программистов, не говоря про обычных пользователей. Но эту задачу можно облегчить, если редактор формул будет помогать автоматически закрывать открытые скобки, а также подсвечивать текущий блок скобок (как это делает Excel, например).
PS. Давно задавался вопросом, но всё как-то лень было копнуть эту тему: позволяют ли средства рефлексии в .NET не только изучать свой собственный код, но и модифицировать его? То есть чтобы пользовательская строка, написанная на одном из языков .NET, могла быть динамически транслирована внутрь исполняемой программы как новая функция или что-то вроде того.
Самое забавное, что нечто похожее было реализовано ещё в древнем ZX Spectrum Бейсике. Там достаточно было бы создать программу:
- Код: Выделить всё
10 INPUT V$
20 FOR X = -10 TO 10 STEP 0.1
30 PLOT (128 + 10 * X), (88 - 10 * VAL(V$))
40 NEXT X
50 GOTO 10
и мы получали бы ровно то, что хотел автор — благодаря тому, что функция VAL(V$) интерпретировала строковое выражение почти как часть программы. После перехода на платформу PC, меня немало удивило, что ни GW-Basic и последующие творения Microsoft, ни тем более TurboBasic (просто потому что он чисто компилирующий), ни другие популярные диалекты не предлагают подобной функциональности.