программистская задачка
Mar. 20th, 2018 07:22 pmКоллега недавно предложил следующее задание, которое мне понравилось в качестве разминки:
Написать код, который вычисляет значение числового выражения, использующего скобки и арифметические действия, используя только константное количество локальных переменных. На любом языке. Код должен печатать результат вычисления, или ошибку (можно неинформативную) в случае некорректного ввода.
На входе: строка типа "(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 (исправлены два бага, спасибо комментаторам!).
Написать код, который вычисляет значение числового выражения, использующего скобки и арифметические действия, используя только константное количество локальных переменных. На любом языке. Код должен печатать результат вычисления, или ошибку (можно неинформативную) в случае некорректного ввода.
На входе: строка типа "(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 (исправлены два бага, спасибо комментаторам!).
no subject
Date: 2018-03-20 05:42 pm (UTC)no subject
Date: 2018-03-20 05:44 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 09:19 pm (UTC) - Expand(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 10:11 pm (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 10:15 pm (UTC) - Expand(no subject)
From:(no subject)
From:no subject
Date: 2018-03-20 05:49 pm (UTC)no subject
Date: 2018-03-20 05:53 pm (UTC)(no subject)
From: (Anonymous) - Date: 2018-03-20 05:58 pm (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 10:08 pm (UTC) - Expand(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-21 03:09 pm (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-21 07:30 am (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 07:16 pm (UTC) - Expand(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-20 10:23 pm (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-22 02:17 am (UTC) - Expand(no subject)
From:(no subject)
From:no subject
Date: 2018-03-20 06:51 pm (UTC)no subject
Date: 2018-03-20 07:04 pm (UTC)no subject
Date: 2018-03-20 08:09 pm (UTC)no subject
Date: 2018-03-20 08:19 pm (UTC)no subject
Date: 2018-03-20 09:42 pm (UTC)если mystring = re.sub(bla, bla, mystring)
за создание новой строки не считается то
https://pastebin.com/tdNw5BB5
можно и без регекспа, но надо строку in-place менять, а в питоне строки иммутабельные
можно только с массивом символов
no subject
Date: 2018-03-21 04:17 am (UTC)(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-21 01:51 pm (UTC) - Expandno subject
Date: 2018-03-24 08:51 am (UTC)no subject
Date: 2018-03-20 10:45 pm (UTC)no subject
Date: 2018-03-21 01:57 am (UTC)(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:(no subject)
From:no subject
Date: 2018-03-21 04:28 am (UTC)no subject
Date: 2018-03-21 11:40 pm (UTC)WTF?
From: (Anonymous) - Date: 2018-03-22 05:09 am (UTC) - ExpandRe: WTF?
From: (Anonymous) - Date: 2018-03-22 06:47 pm (UTC) - ExpandWTF 2
From: (Anonymous) - Date: 2018-03-22 08:56 pm (UTC) - ExpandRe: WTF 2
From: (Anonymous) - Date: 2018-03-22 09:10 pm (UTC) - ExpandRe: WTF 2
From: (Anonymous) - Date: 2018-03-22 09:23 pm (UTC) - Expandno subject
Date: 2018-03-21 07:08 am (UTC)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
no subject
Date: 2018-03-21 09:03 am (UTC)Пожалуйста, не используйте формат JPEG для изображений, не являющихся фотографиями. Он для этого совершенно не приспособлен.
(no subject)
From:(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) - Expandno subject
Date: 2018-03-21 10:26 am (UTC)В этом случае резервируетя память O(n), где n - количество поддерживаемых операторов, т.е. константа. И потом в цикле можно реализовать преобразование инфиксного выражения в постфиксное с вычислением.
А со скобками - только рекурсивный нисходящий парсер, который слишком прост чтобы было интересно тратить на это время ))
no subject
Date: 2018-03-21 12:35 pm (UTC)https://pastebin.com/YNz3mRgq
no subject
Date: 2018-03-22 10:52 pm (UTC)(no subject)
From:no subject
Date: 2018-03-21 04:40 pm (UTC)Это лицемерие.
Любой вызов функции использует стек, в котором хранится "константное [в пределах функции] количество локальных переменных".
Соответственно, если разрешёно неконстатное количество вызовов функций, то де-факто разрешён стек неограниченного размера.
no subject
Date: 2018-03-21 05:20 pm (UTC)no subject
Date: 2018-03-21 08:48 pm (UTC)no subject
Date: 2018-03-21 08:21 pm (UTC)Кажется, есть проблемы с лево-ассоциативностью: 1/1/2 валится с делением на ноль. В самом Питоне, скажем, 1/1/2==0, ну или 1.0/1/2 == 0.5.
no subject
Date: 2018-03-21 08:42 pm (UTC)no subject
Date: 2018-03-21 08:23 pm (UTC)no subject
Date: 2018-03-21 08:48 pm (UTC)Сейчас заменю ссылку в посте на новый пастебин с учетом последних двух комментов.
no subject
Date: 2018-03-22 05:19 am (UTC)Вот вариант на C, который это обрабатывает правильно и при желании превращается в правоассоцивный тривиальной заменой 7 символов.
Он даже слегка попроще всех уже приведённых вариантов, заточенных под левую ассоциативность, и вроде без ошибок.
https://pastebin.com/y94RLZVZ
no subject
Date: 2018-03-22 05:21 am (UTC)(no subject)
From:(no subject)
From: (Anonymous) - Date: 2018-03-22 04:15 pm (UTC) - ExpandИнтересную задачку я пропустил.
Date: 2018-03-23 11:01 am (UTC)Поскольку вы в пояснениях начинаете буквоедствовать (при разрешении рекурсии), то никаких ограничений на память в куче нет.
И тогда классическое: построим AST и вычислим (пусть даже через bison + yacc).
ПС
Ну с рекурсией совсем просто -- такая грамматика стековым автоматом разбирается. Наверное надо тряхнуть стариной и написать (естественно интерес представляет время, в 1ю очередь).
Но вот сейчас этого времени вроде как и нет.
no subject
Date: 2018-03-23 11:47 am (UTC)https://github.com/Alexander-Panin/calculator/blob/master/calculator.py
no subject
Date: 2018-03-23 09:00 pm (UTC)(no subject)
From:no subject
Date: 2018-03-28 10:59 am (UTC)Объясните в двух словах, в чем смысл вычитания int('0') в 12-й и 14-й строках кода решения автора. Спасибо
no subject
Date: 2018-03-28 12:31 pm (UTC)Нет никакого смысла, это у меня, видимо, был мгновенный глюк и я машинально написал то, что всегда пишут в таких случаях в C/C++. Если бы int(c) было ASCII-кодом цифры c, а не самой цифрой, то это вычитание было бы нужным. А так оно ничего не делает. Спасибо, что заметили!