Вызов
Ваша задача состоит в том, чтобы разработать переводчик для языка, похожего на шутки , который в дальнейшем будет придуман: GLisp . Программный код для GLisp будет состоять из произвольного количества вложенных выражений, обозначенных скобками, в следующем виде:
(func arg1 arg2 ...)
Обратите внимание, что интерпретатор должен допускать использование лишних пробельных символов до и после скобок, функций и аргументов.
Типы
Вы будете реализовывать четыре типа: Integer, List, Boolean и Function. Целые числа и логические значения могут быть явно вставлены в исходный код с их собственным синтаксисом. Ваш интерпретатор должен предположить, что серия числовых символов обозначает целое число (вам не нужно реализовывать синтаксис для явной вставки отрицательных целых чисел). Ваш интерпретатор также должен предположить, что true
и false
обозначены как логические значения. Функции не могут быть явно определены пользователем и всегда будут возвращать одно значение (список любой длины считается одним значением).
функции
Следующие функции должны быть реализованы в формате Function , Arity . Если Arity n
сопровождается знаком плюс, то это означает n
или более аргументов. Вы можете предположить, что все аргументы, переданные функции, относятся к одному типу, если не указано иное. Вы также можете предположить, что если не определено поведение для определенного типа, то вы можете предположить, что ни один аргумент этой функции никогда не будет такого типа. Аргументы будут называться на следующей диаграмме:
(func argument1 argument2 ... argumentn)
+ , 2+
- Если все аргументы имеют тип Integer , вы должны вернуть сумму аргументов
- Если все аргументы имеют тип List , вы должны вернуть объединение аргументов в порядке возрастания (
arg1+arg2+ ...
) - Если все аргументы имеют тип Boolean , вы должны вернуть логическую All из последовательности аргументов
(+ 1 2 3 4 5) -> 15
(+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
(+ true true true) -> true
- , 2+
- Если все аргументы имеют тип Integer , вы должны вернуть разницу аргументов (
arg1-arg2- ...
) - Если все аргументы имеют тип Boolean , вы должны вернуть логическую Any из последовательности аргументов
(- 8 4 3) -> 1
(- 0 123) -> -123
(- true false false true false) -> true
- Если все аргументы имеют тип Integer , вы должны вернуть разницу аргументов (
* , 2+
- Если все аргументы имеют тип Integer , вы должны вернуть произведение аргументов
- Если один аргумент имеет тип List, а другой - тип Integer (вы можете предполагать, что это будут только заданные аргументы), вы должны возвращать новый List с элементами в
arg1
повторяющеесяarg2
время. (* 1 2 3 4 5) -> 120
(* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
/ , 2+
- Если все аргументы имеют тип Integer , вы должны вернуть частное значение arguments (
arg/arg2/ ...
) (вы можете предположить, что деление выполняется последовательно, а десятичная часть на каждом шаге усекается) - Если один аргумент относится к типу List, а другой - к типу Function , вы должны вернуть итоговый список после того,
arg2
как он сопоставлен с каждым значением (/ 100 10 3) -> 3
(/ (list 1 2 3) inc) -> (list 2 3 4)
- Если все аргументы имеют тип Integer , вы должны вернуть частное значение arguments (
% , 2
- Если все аргументы имеют тип Integer , вы должны вернуть модуль аргументов
(% 4 2) -> 0
= , 2+
- Если как тип и значение всех аргументов одно и то же, вы должны вернуть истинный. В противном случае верните false.
(= 0 0 0) -> true
(= 0 false (list)) -> false
список , 0+
- Вы должны вернуть список всех аргументов, независимо от типа. Если аргументы не указаны, вы должны вернуть пустой список
(list 3 4 (list 5)) -> (list 3 4 (list 5))
вкл. , 1
- Если аргумент имеет тип Integer , вы должны вернуть Integer, увеличенное на единицу
- Если аргумент имеет тип List , вы должны вернуть список, повернутый по часовой стрелке за один оборот
(inc 1) -> 2
(inc (list 1 2 3)) -> (list 3 1 2)
декабрь 1
- Если аргумент имеет тип Integer , вы должны вернуть Integer, уменьшенное на единицу
- Если аргумент имеет тип List , вы должны вернуть список, повернутый против часовой стрелки за один оборот
(dec 1) -> 0
(dec (list 1 2 3)) -> (list 2 3 1)
если 3
- Если даны три аргумента любого типа: если значение
arg1
true равно true, вернутьarg2
, иначе вернутьarg3
(if (not (list 1)) 8 false) -> false
- Если даны три аргумента любого типа: если значение
нет , 1
- Если задан аргумент любого типа, если значение
arg1
true равно False, вернутьtrue
, иначе вернутьfalse
. (not (list)) -> true
- Если задан аргумент любого типа, если значение
лен , 1
- Если задан аргумент типа List , вернуть длину
arg1
(len (list 4 2 true (list 3) (list))) -> 5
- Если задан аргумент типа List , вернуть длину
Таблица правды:,
0, (list), false -> false
где (list)
обозначает пустой список. Все остальное есть true
.
Ваш интерпретатор может быть либо полной программой, которая читает исходные данные из stdin или файла, либо функцией, которая принимает источник в виде строки и возвращает выходное значение.
Если выбрать первое, для Integer выводится просто число, для Booleans - true
или false
, а для списков - последовательность значений, разделенных пробелами, заключенная в скобки (например, (1 2 3 4 (5 6 7))
обозначает (list 1 2 3 4 (list 5 6 7))
).
При выборе последнего значение должно быть возвращено в соответствующем типе языка реализации или, если нет аналогичного типа, в пользовательском типе. Списки могут быть возвращены как массивы или векторы , если язык не имеет список типа, Booleans должен быть возвращен в качестве логического типа в языке, или типа , если язык не поддерживает их.
Контрольные примеры
(list 1 2 3 (list 4 5 true)) -> (1 2 3 (4 5 true))
(/ 4000 (+ 1 2 3 4 (* 5 8))) -> 80
(+ (not (- (len (list 5 6 7)) (/ 10 3))) true) -> true
(if ( len (list ) ) 4 (if (+ (= 8 8 8) (not (list 4))) 8 5)) -> 5
Разъяснения
- Ваш интерпретатор может иметь дело с неверным вводом любым способом, который вы выберете, но он не должен выдавать исключение (хотя он может распечатать сообщение об ошибке и завершить работу плавно)
- Функции всегда будут оценивать аргументы слева направо
- Неверный ввод - это любой ввод, который синтаксически неверен. Это включает в себя, но не ограничивается, несоответствующие скобки, деление на ноль и частично примененные функции (если не идет на бонус)
- Для
=
, если любое из значений различны или любой из типов различны, возвратfalse
Бонусы
- Оценка * 0,8, если вы поддерживаете частично примененные функции. Например,
((+ 2) 3)
будет так же, как(+ 2 3)
, но учитывает такие вещи, как(/ (list 1 2 3) (+ 2))
. Вы можете предположить, что функция применяется частично, если она получает меньше своего минимального количества аргументов. - Оценка * 0,85, если вы не оцениваете аргументы, применяемые к,
if
если они не будут возвращены
Это код-гольф, поэтому выигрывает интерпретатор с наименьшим количеством байтов!
источник
(if (not (array 1)) 8 false) -> false
?(+ 3 (if false 5))
? Вообще говоря, что на самом деле означает «ничего не возвращать»? Вы не указали тип юнита, который нужно перенастроить(+ bool bool...)
логическое И и(- bool bool...)
логическое ИЛИ? Стандартное обозначение кольца будет использоваться+
для ИЛИ и*
для И. 2. Предназначен ли «неверный ввод» для охвата случаев, подобных(/ 2 0)
синтаксически правильным? 3. Ибо=
, если значения не все одинаковы, должен ли он возвращатьсяfalse
? 4. Определениеnot
представляется задом наперед. 5. Какие токены? Вы говорите, что переводчик должен обрабатывать лишние пробелы, но вы не говорите, на какие пробелы он может положиться. Для сложных вопросов, подобных этому, вы действительно должны использовать песочницу, чтобы можно было проверить спецификацию.((+ 2 3) 4)
равно9
или ошибка? Примечательно, что для функций var-arg не ясно, когда следует рассматривать приложение как частичное. Это становится еще более грязным с такими вещами, как((if true (+ 2 3) (- 5)) 4)
Ответы:
Haskell,
1370126311791128116311071084 байта * 0,8 * 0,85 = 737,12Полная программа, чтение
stdin
и запись вstdout
.g
это версия функции, а также.Реализует как частичные функции, так и ленивую оценку
if
.Примеры прогонов (версии функции):
Теперь есть все юнит-тесты из описания:
источник
e[K,L _]
вы могли бы использоватьdrop 1
в качестве безопасной версииtail
и использоватьtake
в качестве безопасной версииhead
соединения двух определенийe[K,L _]
notElem
Другой совет: вы можете сделатьs=string
и использовать ее вместо обоихstring
иchar
(s"C"
противchar 'C'
). другой совет: используйте охранников вместоif
sMaybe
значения списками.Nothing
есть[]
иJust x
есть[x]
. Это позволяет избавиться от длинных конструкторов и добавляет несколько функций:if p then Just x else Nothing
это[x|p]
,(==Nothing)
естьnull
, список монада становится таким же , как , возможно , монады, и так далее.Python 2, 1417 * 0,8 * 0,85 = 963,56
Капитальный ремонт. Если вы хотите увидеть предыдущие версии, посмотрите историю изменений .
Есть много чего еще в гольфе. Я медленно работаю над этим.
С zlib / base64 получаем 1093 * 0,8 * 0,85 = 743,24 :
Примечание: если вы видите, что мой счет повышается, возможно, это связано с тем, что я нашел несколько ошибок
источник
Common Lisp, 868 байт * 0,85 = 737,8
Это обман для реализации Lisp с Lisp? Здесь все еще много для оптимизации.
Распечатывает E в случае ошибки ввода. Образцы прогонов:
источник
Хаскелл, 972
довольно хакерское решение. это сохраняет все в виде строк в виде, готовом к выводу - их типы можно отличить по первой букве -
0..9
для чисел,(
для списковt
илиf
для логических значений, и все остальное для функций.для запуска используйте
r
функцию.источник