avva: (Default)
[personal profile] avva
Коллега недавно предложил следующее задание, которое мне понравилось в качестве разминки:

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

На входе: строка типа "(2+2)*12" или 56-4*2 или -10^2 итд. Разрешаются скобки и операторы: унарные + и -, бинарные +,-,*,/,^ (возведение в степень). У бинарных операторов обычные уровни приоритета и левая ассоциация внутри уровня, т.е. 10-5-2 равно 3, а не 7. Разрешаются ненужные скобки "(3)". Если в вашем языке нет целочисленной степени, не надо ее имплементировать, можно обойти трюками (напр. в C/C++ перейти к плавающей точке, возвести в степень и округлить). Можно предположить, что все числа и результаты укладываются в размер целочисленного типа вашего языка, если есть такое ограничение. Запрещено использовать массивы, стеки, списки, динамически отведенную память непостоянного размера и другие способы иметь локально неограниченную память в контексте конкретного запуска конкретной функции. Ну и конечно пользоваться чем-то вроде eval() своего языка нельзя, нужно "честно" решить.

Я не утверждаю, что это какое-то особо глубокое или сложное задание, просто хороший способ тряхнуть стариной и элегантно организовать логику. Если вы хотите поучаствовать, присылайте код ссылками на https://pastebin.com/ или свой гитхаб или еще как угодно. Я добавлю ссылку на свое решение (на питоне) завтра вечером к этой записи.

P.S. Поясняю насчет рекурсии. Правила специально сформулированы так ("в контексте конкретного запуска конкретной функции"), что рекурсию МОЖНО использовать. Если вы можете обойтись без рекурсии (я специально не говорю, легко это или тяжело, можно или нельзя), то тоже хорошо, но и рекурсия разрешается.

Update: Спасибо за присланные решения, они все красивые и хорошие :) Мое решение: https://pastebin.com/J6mpMRb2 (исправлены два бага, спасибо комментаторам!).

Date: 2018-03-20 05:42 pm (UTC)
livelight: (lightning)
From: [personal profile] livelight
А в той памяти, где хранится исходное выражение, шалить можно? :)

Date: 2018-03-20 05:44 pm (UTC)
From: [identity profile] avva.livejournal.com
Это извращение мне в голову не приходило, но нет, нельзя :)

(no subject)

From: [personal profile] livelight - Date: 2018-03-20 05:46 pm (UTC) - Expand

(no subject)

From: [identity profile] blainemono.livejournal.com - Date: 2018-03-20 09:08 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-20 09:36 pm (UTC) - Expand

(no subject)

From: [identity profile] jerom.livejournal.com - Date: 2018-03-20 09:38 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 09:19 pm (UTC) - Expand

(no subject)

From: [identity profile] jerom.livejournal.com - Date: 2018-03-20 09:44 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 10:11 pm (UTC) - Expand

(no subject)

From: [identity profile] klvov.livejournal.com - Date: 2018-03-20 07:22 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-20 09:34 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 10:15 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-20 10:33 pm (UTC) - Expand

(no subject)

From: [personal profile] ichthuss - Date: 2018-03-21 06:16 am (UTC) - Expand

Date: 2018-03-20 05:49 pm (UTC)
From: (Anonymous)
Т.е. стек, как структуру данных, использовать нельзя, но стек для вызова функций использовать можно?

(no subject)

From: (Anonymous) - Date: 2018-03-20 05:58 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-20 07:31 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-20 09:44 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 10:08 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-21 01:54 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-21 03:09 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-21 06:25 pm (UTC) - Expand

(no subject)

From: [identity profile] klvov.livejournal.com - Date: 2018-03-21 06:09 pm (UTC) - Expand

(no subject)

From: [personal profile] livelight - Date: 2018-03-21 06:27 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-21 07:30 am (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-21 08:41 am (UTC) - Expand

(no subject)

From: [personal profile] vlad_suh - Date: 2018-03-21 05:12 pm (UTC) - Expand

(no subject)

From: [identity profile] kaathewise.livejournal.com - Date: 2018-03-20 06:13 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-20 07:25 pm (UTC) - Expand

(no subject)

From: [identity profile] kaathewise.livejournal.com - Date: 2018-03-20 07:44 pm (UTC) - Expand

(no subject)

From: [identity profile] nefedor.livejournal.com - Date: 2018-03-20 10:23 pm (UTC) - Expand

(no subject)

From: [identity profile] kaathewise.livejournal.com - Date: 2018-03-20 11:31 pm (UTC) - Expand

(no subject)

From: [identity profile] nefedor.livejournal.com - Date: 2018-03-21 01:27 am (UTC) - Expand

(no subject)

From: [identity profile] dims12.livejournal.com - Date: 2018-03-20 06:18 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-20 07:24 pm (UTC) - Expand

(no subject)

From: [identity profile] dims12.livejournal.com - Date: 2018-03-20 07:34 pm (UTC) - Expand

(no subject)

From: [identity profile] deep-econom.livejournal.com - Date: 2018-03-20 06:46 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 07:16 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-20 07:33 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-20 10:23 pm (UTC) - Expand

(no subject)

From: [identity profile] urod.livejournal.com - Date: 2018-03-21 03:25 pm (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-22 01:48 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-22 02:17 am (UTC) - Expand

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-22 10:10 am (UTC) - Expand

(no subject)

From: [personal profile] vlad_suh - Date: 2018-03-22 08:31 am (UTC) - Expand

Date: 2018-03-20 06:51 pm (UTC)
From: [identity profile] ordinary-joe-1.livejournal.com
есть такой язык брейнфак. я даж в своё время на нем хело ворлд исполнил. было тяжело!

Date: 2018-03-20 07:04 pm (UTC)
From: [identity profile] xxxxx.livejournal.com
пробежаться раз по строке и найти операцию с наименьшим приоритетом не защищённую скобками, из равных приоритетов выбирать правую. Вызваться рекурсивно вокруг неё и всё.

Date: 2018-03-20 08:09 pm (UTC)
From: [identity profile] a-konst.livejournal.com
Использование рекурсии алгоритмически равносильно использованию стека, так что это разрешение очень странно.

Date: 2018-03-20 09:42 pm (UTC)
From: [identity profile] vissarion.livejournal.com
можно и без рекурсии
если mystring = re.sub(bla, bla, mystring)
за создание новой строки не считается то

https://pastebin.com/tdNw5BB5

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

можно только с массивом символов

Edited Date: 2018-03-21 01:20 am (UTC)

Date: 2018-03-21 04:17 am (UTC)
From: (Anonymous)
Код подвисает на 2^(-1). На 2^-1, 2^--1 возвращает 1.

(no subject)

From: [identity profile] vissarion.livejournal.com - Date: 2018-03-21 01:02 pm (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-21 01:51 pm (UTC) - Expand

Date: 2018-03-24 08:51 am (UTC)
From: (Anonymous)
Выдаёт -1 вместо 0, как будто у + и ^ приоритеты совпадают.

Date: 2018-03-20 10:45 pm (UTC)
From: [identity profile] gul-kiev.livejournal.com
Из классических программистских задач мне нравится "напишите (на произвольном языке) программу, выводящую собственный исходный текст". С очевидными ограничениями: без чтения файлов и т.п.

Date: 2018-03-21 01:57 am (UTC)
From: (Anonymous)
Ещё вариант: есть функция rand(), которая равновероятно возвращает целое от 0 до R–1. Написать функцию random(n), которая равновероятно возвращает целое от 0 до n–1, вызывая rand() в среднем минимально возможное число раз. Желательно получить элегантный код с доказательством оптимальности.

(no subject)

From: (Anonymous) - Date: 2018-03-21 10:37 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-21 11:30 am (UTC) - Expand

(no subject)

From: [identity profile] gul-kiev.livejournal.com - Date: 2018-03-21 03:58 pm (UTC) - Expand

(no subject)

From: [identity profile] dbg.livejournal.com - Date: 2018-03-21 12:37 pm (UTC) - Expand

Date: 2018-03-21 04:28 am (UTC)
From: (Anonymous)
https://pastebin.com/0A5kArWf

Date: 2018-03-21 11:40 pm (UTC)
From: [identity profile] avva.livejournal.com
Вау, это круто. Мне очень понравилось. Я хотел сделать версию, где рекурсивный вызов возвращает только значение и больше ничего, но когда запутался с приоритетами, плюнул и сделал возвращение оставшейся строки тоже. А у вас получилось. Код для поиска места, где можно разбить, нелегко так сразу понять, но по-моему все работает. Спасибо!

WTF?

From: (Anonymous) - Date: 2018-03-22 05:09 am (UTC) - Expand

Re: WTF?

From: (Anonymous) - Date: 2018-03-22 06:47 pm (UTC) - Expand

WTF 2

From: (Anonymous) - Date: 2018-03-22 08:56 pm (UTC) - Expand

Re: WTF 2

From: (Anonymous) - Date: 2018-03-22 09:10 pm (UTC) - Expand

Re: WTF 2

From: (Anonymous) - Date: 2018-03-22 09:23 pm (UTC) - Expand

Date: 2018-03-21 07:08 am (UTC)
From: [identity profile] elfadmin.livejournal.com
А expression tree можно? А то у меня готовый пример к моей библиотеке state machines, ( да, я знаю что грамматика конечным автоматом не описывается :-))
https://github.com/blitvin/fsm4java/tree/master/src/test/java/org/blitvin/statemachine/expressionparser и даже с картинками и описанием https://github.com/blitvin/fsm4java/blob/master/docs/expression_parser.md

Date: 2018-03-21 09:03 am (UTC)
From: (Anonymous)
> даже с картинками

Пожалуйста, не используйте формат JPEG для изображений, не являющихся фотографиями. Он для этого совершенно не приспособлен.

(no subject)

From: [identity profile] elfadmin.livejournal.com - Date: 2018-03-21 09:44 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-21 10:32 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-22 10:03 pm (UTC) - Expand

Date: 2018-03-21 10:26 am (UTC)
From: [identity profile] amosk.livejournal.com
Если бы не было скобок, то можно было бы обойтись без рекурсии.
В этом случае резервируетя память O(n), где n - количество поддерживаемых операторов, т.е. константа. И потом в цикле можно реализовать преобразование инфиксного выражения в постфиксное с вычислением.

А со скобками - только рекурсивный нисходящий парсер, который слишком прост чтобы было интересно тратить на это время ))

Date: 2018-03-21 12:35 pm (UTC)
From: [identity profile] litwak.livejournal.com
На C
https://pastebin.com/YNz3mRgq

Date: 2018-03-22 10:52 pm (UTC)
From: (Anonymous)
1-1-1 = 1

(no subject)

From: [identity profile] litwak.livejournal.com - Date: 2018-03-23 11:59 am (UTC) - Expand

Date: 2018-03-21 04:40 pm (UTC)
From: (Anonymous)
> Запрещено использовать массивы, стеки

Это лицемерие.
Любой вызов функции использует стек, в котором хранится "константное [в пределах функции] количество локальных переменных".
Соответственно, если разрешёно неконстатное количество вызовов функций, то де-факто разрешён стек неограниченного размера.

Date: 2018-03-21 05:20 pm (UTC)
vlad_suh: Glider in the sky (планер)
From: [personal profile] vlad_suh
Модифицировать исходную строку заменой тоже нельзя?

Date: 2018-03-21 08:48 pm (UTC)
From: [identity profile] avva.livejournal.com
Нет.

Date: 2018-03-21 08:21 pm (UTC)
From: (Anonymous)
Анатолий, спасибо за Ваше решение.

Кажется, есть проблемы с лево-ассоциативностью: 1/1/2 валится с делением на ноль. В самом Питоне, скажем, 1/1/2==0, ну или 1.0/1/2 == 0.5.

Date: 2018-03-21 08:42 pm (UTC)
From: [identity profile] avva.livejournal.com
Спасибо! Фиксится заменой всех условий типа "if incoming_rank > 1" на >=.

Date: 2018-03-21 08:23 pm (UTC)
From: (Anonymous)
evalexpr("-5+1") = -6, печаль.

Date: 2018-03-21 08:48 pm (UTC)
From: [identity profile] avva.livejournal.com
Спасибо! Фиксится заменой incoming_rank=1 на incoming_rank=2 в 8-й строке.

Сейчас заменю ссылку в посте на новый пастебин с учетом последних двух комментов.

Date: 2018-03-22 05:19 am (UTC)
From: (Anonymous)
Мне не нравится, что у вас возведение в степень левоассоциативно, это противоречит общепринятым стандартам.
Вот вариант на C, который это обрабатывает правильно и при желании превращается в правоассоцивный тривиальной заменой 7 символов.
Он даже слегка попроще всех уже приведённых вариантов, заточенных под левую ассоциативность, и вроде без ошибок.

https://pastebin.com/y94RLZVZ

Date: 2018-03-22 05:21 am (UTC)
From: (Anonymous)
Пардон, "превращается в левоассоциативный" (сейчас там возведение в степень как раз правоассоциативно).

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2018-03-22 11:06 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2018-03-22 04:15 pm (UTC) - Expand
From: [identity profile] son-0f-morning.livejournal.com
>> используя только константное количество локальных переменных.

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

И тогда классическое: построим AST и вычислим (пусть даже через bison + yacc).

ПС
Ну с рекурсией совсем просто -- такая грамматика стековым автоматом разбирается. Наверное надо тряхнуть стариной и написать (естественно интерес представляет время, в 1ю очередь).
Но вот сейчас этого времени вроде как и нет.


Date: 2018-03-23 11:47 am (UTC)
From: [identity profile] xyli0o.livejournal.com
Спасибки за задачку
https://github.com/Alexander-Panin/calculator/blob/master/calculator.py

Date: 2018-03-23 09:00 pm (UTC)
From: (Anonymous)
Прямо в одном из ваших же тестов ответ неправильный.

(no subject)

From: [identity profile] xyli0o.livejournal.com - Date: 2018-03-24 06:59 am (UTC) - Expand

Date: 2018-03-28 10:59 am (UTC)
From: [identity profile] zz-pten.livejournal.com
Спасибо, очень интересно.

Объясните в двух словах, в чем смысл вычитания int('0') в 12-й и 14-й строках кода решения автора. Спасибо

Date: 2018-03-28 12:31 pm (UTC)
From: [identity profile] avva.livejournal.com
Ахаха :)

Нет никакого смысла, это у меня, видимо, был мгновенный глюк и я машинально написал то, что всегда пишут в таких случаях в C/C++. Если бы int(c) было ASCII-кодом цифры c, а не самой цифрой, то это вычитание было бы нужным. А так оно ничего не делает. Спасибо, что заметили!

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
2829 30 31   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 2nd, 2026 06:55 am
Powered by Dreamwidth Studios