Итак, я хочу иметь возможность анализировать и оценивать "кубические выражения" на С#. Выражение кости определяется следующим образом:
<expr> := <expr> + <expr>
| <expr> - <expr>
| [<number>]d(<number>|%)
| <number>
<number> := positive integer
Так, например, d6+20-2d3
будет разрешено и должно оцениваться как
rand.Next(1, 7) + 20 - (rand.Next(1, 4) + rand.Next(1, 4))
Также d%
должен быть эквивалентен d100
.
Я знаю, что я мог бы взломать какое-то решение, но я также знаю, что это похоже на очень типичную проблему типа компьютерного типа, поэтому должно быть какое-то очень элегантное решение, которое я должен изучить.
Я хочу, чтобы результат моего разбора имел следующие возможности:
- Я должен иметь возможность выводить нормализованную форму выражения; Сначала я думаю о кубиках, отсортированных по размеру в кости, и всегда с префиксом. Так, например, вышеприведенный образец станет
1d6-2d3+20
. Также любые экземплярыd%
станутd100
в нормализованной форме. - Я должен уметь оценивать выражение at-will, качая различные случайные числа каждый раз.
- Я должен уметь оценивать выражение с максимальным увеличением всех кубиков, так, например, образец выше дал бы (детерминистически)
1*6+20+2*3 = 32
.
Я знаю, что это именно тот тип вещей, который Haskell и, возможно, другие языки функционального типа, были бы хороши, но я хотел бы остаться на С#, если это возможно.
Мои первоначальные мысли имеют тенденцию к рекурсии, спискам и, возможно, некоторым LINQ, но опять же, если я пробовал без каких-либо указателей от людей, которые знают вещи, я уверен, что это закончится тем, что это нечеткий беспорядок.
Другая тактика, которая могла бы работать, - это некоторая начальная замена строк на основе регулярных выражений, чтобы превратить выражения в кости в вызовы rand.Next
, а затем на лету оценку или компиляцию... будет ли это действительно работать? Как я мог избежать создания нового объекта rand
каждый раз?