Pull to refresh

Comments 22

Что-то ваш парсер получился ничуть не простой...


                        char[] buffer = new char[functionEnd - functionStart - 1];
                        expression.CopyTo(functionStart + 1, buffer, 0, functionEnd - functionStart - 1);
                        string functionParametrs = new string(buffer);

Кто-нибудь, расскажите автору о существовании метода Substring!


                        result.RemoveRange(result.Count - 1, 1);

И о методе RemoveAt тоже нужно напомнить...

Боюсь, это не получится сдать в качестве домашней работы)
Ваше нормальному преподавателю — тоже сдать не получится.
Ну можно же погуглить, почитать про рекурсивный спуск или сортировочную станцию, и не изобретать неработающее непонятно что.

Он даже близко не подобрался к его изобретению.

Shunting-yard это алгоритм для инфиксной нотации. Автор же просто отказался от попытки ее разбора в пользу польской нотации, где порядок выполнения операций определяется их порядком в записи.
julia> str = "(2 + 2) * ((2 * 2) + ((2 * 2) * (2 * 2)))" # строка с арифметикой
"(2 + 2) * ((2 * 2) + ((2 * 2) * (2 * 2)))"

julia> Meta.parse(str)
:((2 + 2) * (2 * 2 + (2 * 2) * (2 * 2)))

julia> eval(ans)
80

julia> str2 = replace(str, '*' => "+ 2 +")
"(2 + 2) + 2 + ((2 + 2 + 2) + ((2 + 2 + 2) + 2 + (2 + 2 + 2)))"

julia> str2 |> Meta.parse |> eval # цепочка функций
26

Решать через аналог eval можно и в C#. Но все такие решения плохи по нескольким причинам:


  • ты привязан к грамматике своего языка, не можешь её расширить,
  • пользователь может вместо арифметического выражения отправить что-то с побочным эффектом (если eval полноценный).
Не смог прочитать ваш код. Впрочем, у меня и свой не всегда получается читать.:-)

Когда то на меня произвел большое впечатление курс по компиляторам.
Т.е. как классно и гладко распарсивается синтаксис программы.
Арифметическое выражение это явно проще чем программа.
Рекурсивное чтение будет на ура проходить.

Уверен, в инете много примеров, и если вы хотите сделать действительно красиво — попробуйте посмотреть в эту сторону.
((((((((((((((((((И скобки считать не придется...))))))))))))))))))))))

Теперь про ошибки в алгоритме.


  1. "Естественно, если мы встретили знак "+" или "-" не после цифры, значит этот знак обозначает положительность или отрицательность числа, соответственно." — нет, не естественно. Смотрите: sin(1) - 2


  2. С каких пор у функции sqrt есть второй аргумент? Напоминаю, что название этой функции — аббревиатура от "square root", что переводится как "квадратный корень"


  3. В польской нотации можно записывать не только "простые" формулы, а вообще любые, и без скобок. Так, ваш пример sqrt(2*2; log(4;2)) может быть записан как 2 2 * 4 2 log sqrt. Отсюда вопрос: зачем вам вообще понадобилась большая часть алгоритма? Зачем все эти поиски парных скобок, нахождение параметров и прочее?


Спасибо! Обязательно учту Ваши замечания!
UFO just landed and posted this here
Надо бы мне свой курсовичок залить с генератором компиляторов чисто на Qt без внешних зависимостей вроде бизона)
А так да, тортовость падает
Понимаю, что это учебный проект. Однако ведь пользователю интересен не код, а результат. Чтобы проверить выпоняет ли код ожидания пользователя — нужны юнит-тесты. Тогда хоть как-то можно будет понять для каких конкретных случаев этот код работает. А так это черный ящик. С неопределенным количеством багов внутри. От этого есть и побочный положительный эффект для разработчика. Начнете писать тесты — лучше станете понимать логику приложения. Ну и если захотите поменять реализацию — с тестами будет куда проще убедиться что оно все еще делает то что должно.
А чем вас рекурсивный спуск не устроил? Просто и элегантно, если не разбирать гигантские выражения — то и с памятью тоже будет всё в порядке.

Antlr, вроде, как один из самых популярных. И nuget-пакет для него есть даже. Понятно, что любой парсер требует порог вхождения, но поверьте, что самостоятельная обработка ошибок намного сложнее самописного парсера.
Я однажды сам попытался написать простой парсер типа вашего. Больше таких глупостей не делаю. )))
Как способ посмотреть, насколько это сложное и неблагодарное занятие подходит, но для чего-то в продакшен не пойдёт. Попробуйте добавить хоть одну «переменную» и все придётся переписать.
Почитайте что-нибудь по грамматикам.

Вытеки мои глазоньки… Действительно, решение — «оригинальное» — в том смысле, что второго такого вряд ли найдете. Если целью учебного задания было научить вас работать со строками и стандартными структурами данных в C#, то, наверное, все ок, а если — научиться писать парсер, то так парсер не пишется.

Вы допустили несколько ошибок — методологических в основном.

1. Любой парсер начинается с юнит-тестов! Для начала хотя бы пары выражений, но в идеале — чем больше, тем лучше. Потратьте полчаса времени придумывая самые извратные варианты — потом это окупится с лихвой.
2. За юнит-тестами идет грамматика. Честно говоря, я был удивлен, не увидев её в статье — может, вам её уже дали в условиях задания? Если нет — обязательно нужно описать грамматику и отладить её на юнит-тестах — в инете есть для этого средства, ну или написать свои.
3. Далее — лексер. Он разбивает выражение на токены, затем парсер работает уже с ними. Одновременно токенизировать и парсить как минимум неудобно.
4. Наконец — сам парсер. Изобретать велосипед — дело, конечно, интересное, но в вопросе парсинга математических выражений — мягко говоря, неблагодарное. Все уже было украдено до нас. Задолго. Просто реализуйте алгоритм рекурсивного спуска или сортировочной станции и радуйтесь результату.
Ну у нас например на втором курсе был преподаватель, который давал это задание как «желательно сделать», для математических лабораторных с построением графиков. А грамматики были на четвертом.
Если это С#, то можно воспользоваться методом Compute класса DataTable

static Double Eval(String expression)
{
System.Data.DataTable table = new System.Data.DataTable();
return Convert.ToDouble(table.Compute(expression, String.Empty));
}
...
Double result = Eval("(2 + 2) * ((2 * 2) + ((2 * 2) * (2 * 2)))");
...
Sign up to leave a comment.

Articles