Pull to refresh

Comments 60

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

Кажется, что всё не так просто. Там ещё с приоритетом операций будет проблема

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

Нам прямо предписывается использовать для обозначения унарного минуса любой свой придуманный символ. Давайте договоримся, что это будет тильда ~.


Унарный плюс не нужен, его можно просто найти и убрать, а унарный минус можно найти, убрать, и провести замену соответствующих последующих плюсов и минусов на противоположные знаки.
Читайте, пожалуйста, внимательнее
Опять же всё давно изобретено до нас, использовать мы будет классический алгоритм сортировочной станции. Просто распишем его максимально подробно и понятно.


унарный минус можно найти, убрать, и провести замену соответствующих последующих плюсов и минусов на противоположные знаки

Это как, приведите пример. Пожалуйста.
Например выражение -2 - -2
Что мы должны сделать с унарным минусом в начале строки?
Любопытная конечно запись, и какой смысл вот так писать унарные минусы: -2 - -2? Предполагаю что в итоге будет - 2 + 2.

Кстати вариант про добавление эфемерного ноля, который тут упоминали в комментариях, тоже интересный, меньше мороки.
Вы не ответили на мой вопрос.
Любопытная конечно запись, и какой смысл вот так писать унарные минусы: -2 — -2

Что значит в чем смысл? Мы никогда не знаем, что придет нам на вход.
Вы не ответили на мой вопрос.


Вот ведь ответ: - 2 + 2. А если минус перед скобками, то это школьная арифметика, гуглить по словам: правило раскрытия скобок, перед которыми стоит знак минус

Что значит в чем смысл? Мы никогда не знаем, что придет нам на вход.


Для такого входные данные и нужно валидировать.
а унарный минус можно найти, убрать, и провести замену соответствующих последующих плюсов и минусов на противоположные знаки.

Еще раз: пожалуйста приведите алгоритм того, что вы написали
Ай-яй
Вопрос. Ответ на него очень простой — будет 2

Как-то даже неудобно себя чувствую
По правилам арифметики правильный ответ 0.25
Вообще-то, -0.25
Но это был контрпример к фразе Zaphkiel
провести замену соответствующих последующих плюсов и минусов на противоположные знаки
Не всё так просто с заменой всех подряд знаков.
Упс, делаю несколько дел одновременно, конечно же -0.25
Спасибо
Там комментарий был не к этому. Тут разумеется будет -0.25.

Странные у вас задачки для повышения. Это же типичная задача для первого-второго курса какого-нибудь универа.

Возможно это и есть повышение до студента второго-третьего курса :)

Статья хотя и рассматривает тему, которая в интернете и различных учебниках раскрыта уже множество раз, но все же может кому-то пригодиться.


Вопрос в другом: зачем у вас в компании для повышения на следующий грейд решать какие-то выдуманные задачи, если процессу повышения предшествует как минимум полгода работы сотрудника? Разве его результатов за период не достаточно для принятия решения? В чем ценность того, что человек смог написать алгоритм калькулятора?

Надо убедиться что сотрудник способен хотя бы месяц заниматься подготовкой алгоритмов вместо работы и чтоб никто этого не замечал. Софт-скиллз!

Ну так-то время на решение задачи ограничено
да, поэтому надо подготовиться пару месяцев к ее выдаванию
Не могу ответить на ваш вопрос. Есть установленный руководством порядок проведения аттестации, есть корпоративная этика.

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

Зумеры открыли для себя принцип работы калькуляторов БЗ-21, Б3-34 итд

Для красивого вычисления арифметического выражения в виде строки необходимо:
Необходимо — слишком сильно сказано, скорее достаточно. Приведение к ОПЗ — это лишь один из вариантов, да и то, вместо формирования строки можно записывать числа во второй стек и все операции сразу же выполнять.
А ещё можно написать грамматику арифметических выражений и по ней разбирать выражение рекурсивным спуском или построить соответствующий грамматике МП-автомат.
Почему-то по постановке тоже подумал про граф синтаксического анализа и автомат на его основе.
3 или 4 курс ВУЗа…

Но нет пишем опять на куче ифов и погрязаем в обработке ошибок.

Ох сколько уже людей топтали эти грабли (каюсь сам — один из них), но ёжики продолжают колоться и есть кактусы.
Для красивого вычисления арифметического выражения в виде строки необходимо:
  1. Разобраться что такое Обратная польская запись, и почему она идеально подходит для машинных вычислений
  2. Привести арифметическое выражение к ОПЗ, и вычислить его

А я-то подумал, надо построить дерево выражений, а потом его вычислить...

И дерево, и любая из польских записей — эквивалентные формы представления выражения. Но дерево удобнее для дальнейшей оптимизации вычислений, а ОПЗ — для непосредственного вычисления as is.
Ладно, сторонние библиотеки использовать нельзя… Но стандартные, встроенные в язык механизмы — они тоже запрещены?

Зачем делать кривой лексический анализатор, когда в язык PHP встроен www.php.net/manual/ru/book.tokenizer.php, прекрасно подходящий для разбора выражений?

Зачем эмулировать стеки массивами, когда в язык встроен www.php.net/manual/ru/class.splstack.php?

Для машины символ минус — это всегда оператор вычитания. Ни о каких унарных минусах машина не знает.
Машина вообще не знает, что такое «минус». Она знает процессорный код команды вычитания и процессорный код команды изменения знака числа. Если данная процессорная архитектура не имеет команды изменения знака — компилятор подставит команду вычитания из нуля. Если имеет — компилятор воспользуется командой изменения знака.
Давайте рассмотрим простейший пример:
$a = -2
Что происходит в данном примере, с точки зрения машины?
C точки зрения машины — ничего не происходит. А с точки зрения генератора исполняемого кода будет вычислено константное выражение -2 и в генерируемую последовательность опкодов будет записана единственная команда: запись значения константы в переменную.
Токенизатор PHP требует на входе PHP-код, а не обычную строку. С этим то бороться несложно, достаточно дописать в начало строки '<?php ', а в конец ';'. Но ещё он не распознаёт лексемы арифметических операций, возвращая вместо них обычные строки. Так что после него всё равно нужен дополнительный анализ.
Пример работы токенизатора
<?php
$tokens = token_get_all("<?php 2+2*2;");
foreach ($tokens as $token) {
  if (is_string($token)) {
    print "STR: '{$token}'\n";
  } else {
    print token_name($token[0]) . ": '{$token[1]}', $token[2]\n";
  }
}
/*
T_OPEN_TAG: '<?php ', 1
T_LNUMBER: '2', 1
STR: '+'
T_LNUMBER: '2', 1
STR: '*'
T_LNUMBER: '2', 1
STR: ';'
*/

Лучше вот так: "<?php 2+2*2 ?>". А точка с запятой не требуется.

Токен, состоящий из единственного символа, сам по себе является своим кодом. Так что создатели лексера не стали заморачиваться лишними сущностями. Но в любом случае, это будет проще, чем код, предлагаемый автором статьи.
Огромное спасибо за комментарий.
с точки зрения генератора исполняемого кода будет вычислено константное выражение -2 и в генерируемую последовательность опкодов будет записана единственная команда: запись значения константы в переменную

Из моего окружения никто не смог объяснить почему интерпретатор php всегда неверно считает выражение:
<?php
echo -2 ** 2;

Объясните, пожалуйста, что такого в этом выражении, что -2 не вычисляется? Мне действительно интересно. Заранее спасибо.

"Из моего окружения никто не смог объяснить"


Если окружение с этим самым php не связано — это норма. Иначе даже и не знаю, как.это прокомментировать О_о.

Хм, не надо никогда делать скоропалительных выводов, операторы * и / тоже имеют больший приоритет
<?php
echo -2 ** 2; // -4 неверно
echo PHP_EOL;
echo -2 ** -2; // -0.25 верно
echo PHP_EOL;
echo -2 * -2; // 4 верно
echo PHP_EOL;
echo -2 / -2; // 1 верно
echo PHP_EOL;
Нет, унарный минус (третья строка сверху) имеет больший приоритет, чем умножение и деление (шестая строка сверху).
Почему в выражении -2 ** 2 унарный минус имеет меньший приоритет, а выражении -2 ** -2 бОльший?
С чего вы взяли?
-2 ** 2 = — (2 ** 2) = -4
-2 ** -2 = — (2 ** (-2)) == -0.25
Или вы хотите спросить, почему во втором случае второй унарный минус считается раньше возведения в степень? Потому что операция возведения считается справа налево.
Спасибо. В любом случае php интерпретатор неверно высчитывает выражение
-2 ** 2
Это я к чему? Самым простым решением для задачи вычислить выражение из строки будет:
1. Провалидировать строку, и если выражение верное
2. Создать на лету php файл, записать в него выражение, и через exec получить результат вычисления

И не надо заморачиваться с алгоритмом.
Но:
1. Это неинтересно :)
2. Результат может быть неверным (-2 ** 2)
В любом случае php интерпретатор неверно высчитывает выражение -2 ** 2
Верно считает. Не так, как вы представляли себе, но верно. Ещё в школе нас учили, что если основание степени отрицательное число, то при записи его всегда берут в скобки (-2)2.
(-2) ** 2 = 4
-2 ** 2 = -(2 ** 2) = -4
Стоп! Вот некая компания заказала нам небольшой проект, калькулятор вычисления стоимости металлических дверей.
Вы точно уверены, что все манагеры напишут (-2) ^ 2?
Или они напишут -2 ^ 2?

Ни то, ни другое, потому что это не надо для вычисления стоимости.


При разработке такого калькулятора задача как раз в том, чтобы нельзя было ошибиться или ввести неоднозначное выражение.

Какое-то непонимание. Я фиг его знает, что прилетит снаружи.
Если, это верное арифметическое выражение, то считаем.
Иначе выкидываем исключение.

Выражение -2 ** 2 априори считается верным.
Выражение (-2) ** 2 тоже верным

В первом случае php посчитает неверно. Во втором — верно. Кто неправ?
Какое-то непонимание. Я фиг его знает, что прилетит снаружи.

В "калькуляторе вычисления стоимости металлических дверей"? Почему?


В первом случае php посчитает неверно.

Почему неверно-то?


Кто неправ?

Права спецификация, в которой указано, как такие случаи должны обрабатываться.

Ок, полностью абстрагируемся от внешнего мира. К нам прилетает строка -2 ^ 2
Какой процент пользователей поставят её в скобки?
(-2) ^ 2
Все, кто ещё не забыл школьный курс математики. К счастью, правила вычисления в математике пока устанавливаются не большинством голосов. Иначе давно уже было бы 2+2=22.
Пруф, пожалуйста, дайте, на ваш постулат: при написании строки с арифметическим выражением, если мы возводим отрицательное число в какую-либо степень, то мы должны заключить данный операнд в скобки
О_о
если мы возводим отрицательное число в какую-либо степень, то мы должны заключить данный операнд в скобки

Ну вы даёте…

-2**2
0-2**2


Это одно и то же или нет?
Ок, полностью абстрагируемся от внешнего мира [...] Какой процент пользователей поставят её в скобки?

Пользователи — это и есть внешний мир. Вы либо абстрагируетесь, либо нет.

Отлично! Спасибо Вам огромнейшее!!! Вроде говорим об элементарных вещах, а как интересно! Спасибо еще раз!!! Буду пытаться думать :)
Какое-то непонимание. Я фиг его знает, что прилетит снаружи.
Калькулятор вычисления стоимости металлических дверей, пластиковых окон, теплиц, и т.д., и т.п., никакого отношения к вычислению арифметического выражения в строке не имеет.
В таком калькуляторе вам приходит набор параметров двери — модель полотна, высота, ширина, модель замка, модели личинок, модель ручек, тип петель, наличие и типы декоративных элементов. Вам остаётся только провести калькуляцию по этим параметрам и выдать результат.
Да, кстати, попробуйте написать -2 ** 2 в JavaScript. Он вообще скажет, что видит неопределённость и потребует явно скобками указать порядок вычислений.
>> -2 ** 2
! Uncaught SyntaxError: unparenthesized unary expression can't appear on the left-hand side of '**'
Кстати да, пробовал. В консоли прямо написать. В js эту ситуацию обработали. В php — нет.
А в PHP взяли стандартное поведение из математики (к вопросу, зачем программисту знать математику :-)).
А ничего, что для ваших примеров именно для * и / соотношение приоритетов на результат не влияет?
php > echo -(2 * -2);
4
php > echo (-2) * -2;
4
php > echo -(2 / -2);
1
php > echo (-2) / -2;
1
Вообще-то это объясняется в официальной документации PHP:
www.php.net/manual/ru/language.operators.precedence.php
В PHP единая таблица приоритетов для унарных и бинарных операций. И приоритет бинарной операции может быть выше приоритета унарной — как и произошло с возведением в степень.
Да, тем, кто привык к безусловной приоритетности унарных операций, ломает шаблон.
Sign up to leave a comment.

Articles