(~!)(!)((~)~*):((!)~^)*(:^)(~(!)~^(~)~*)(()~(~)~^~*)
Попробуйте онлайн! (включает тестовый набор и текстовые части программы)
Это удивительно хорошо для esolang очень низкого уровня. (По этой причине в Underload очень часто используются церковные цифры, церковные логические значения и т. Д .; в языке нет встроенных чисел и логических значений, и это один из самых простых способов их симуляции. закодируйте логические числа как церковные цифры 0 и 1.)
Для тех, кто в замешательстве: недогрузка позволяет вам определять многократно используемые функции, но не позволяет вам называть их обычным способом, они просто как бы плавают в стеке аргументов (так что если вы определяете пять функций, а затем хотите вызвать первую Вы определили, что вам нужно написать новую функцию, которая принимает пять аргументов и вызывает пятый из них, а затем вызывает ее с недостаточным количеством аргументов, чтобы она могла искать свободные аргументы). Вызов их уничтожает их по умолчанию, но вы можете изменить вызов, чтобы сделать его неразрушающим (в простых случаях вам просто нужно добавить двоеточие к вызову, хотя сложные случаи встречаются чаще, потому что вам нужно убедиться, что копии в стеке, не мешайте), так что поддержка функций Underload имеет все требования, которые нам нужны из этого вопроса.
объяснение
правда
(~!)
( ) Define function:
~ Swap arguments
! Delete new first argument (original second argument)
Это довольно просто; мы избавляемся от аргумента, который нам не нужен, и аргумента, который нам нужен, просто остается там, служа в качестве возвращаемого значения.
ложный
(!)
( ) Define function:
! Delete first argument
Это еще проще.
не
((~)~*)
( ) Define function:
~* Modify first argument by pre-composing it with:
(~) Swap arguments
Это забавно: он not
вообще не вызывает свой аргумент, он просто использует композицию функций. Это обычная уловка в Underload, в которой вы вообще не проверяете свои данные, вы просто меняете их функционирование, предварительно и после компоновки. В этом случае мы модифицируем функцию, чтобы поменять ее аргументы перед запуском, что явно отрицает церковную цифру.
а также
:((!)~^)*
( ) Define function:
~^ Execute its first argument with:
(!) false
{and implicitly, our second argument}
* Edit the newly defined function by pre-composing it with:
: {the most recently defined function}, without destroying it
Вопрос позволяет определять функции в терминах других функций. Мы определяем «и» далее, потому что чем позже было определено «нет», тем легче его использовать. (Это не вычитает из нашей оценки, потому что мы вообще не называем «не», но экономит байты при повторной записи определения. Это единственный раз, когда одна функция ссылается на другую, потому что ссылается на любую функцию но последнее определение будет стоить слишком много байтов.)
Определение здесь and x y = (not x) false y
. Другими словами, если not x
, тогда мы вернемся false
; в противном случае мы вернемся y
.
или
(:^)
( ) Define function:
: Copy the first argument
^ Execute the copy, with arguments
{implicitly, the original first argument}
{and implicitly, our second argument}
@Nitrodon указал в комментариях, что or x y = x x y
обычно короче or x y = x true y
, и это оказывается правильным и в Underload. Наивная реализация этого была бы(:~^)
, но мы можем отыграть дополнительный байт, отметив, что не имеет значения, запускаем ли мы первый аргумент или его копию, результат одинаков в любом случае.
Недостаточная нагрузка на самом деле не поддерживает карри в обычном смысле, но такие определения заставляют ее выглядеть так, как будто это происходит! (Хитрость заключается в том, что неиспользуемые аргументы просто остаются рядом, поэтому вызываемая вами функция будет интерпретировать их как свои собственные аргументы.)
подразумевает
(~(!)~^(~)~*)
( ) Define function:
~ Swap arguments
~^ Execute the new first (original second) argument, with argument:
(!) false
{and implicitly, our second argument}
(~)~* Run "not" on the result
Определение , используемое здесь implies x y = not (y false x)
. Если у истинно, это упрощается not false
, то есть true
. Если у ложно, это упрощается not x
, давая нам таблицу истинности, которую мы хотим.
В этом случае мы используем not
снова, на этот раз переписывая его код, а не ссылаясь на него. Он просто написан напрямую, (~)~*
без скобок, поэтому вызывается, а не определяется.
исключающее
(()~(~)~^~*)
( ) Define function:
~ ~^ Execute the first argument, with arguments:
(~) "swap arguments"
() identity function
~* Precompose the second argument with {the result}
На этот раз мы оцениваем только один из двух наших аргументов и используем его, чтобы определить, что нужно составить для второго аргумента. Недогрузка позволяет вам играть быстро и свободно с арностью, поэтому мы используем первый аргумент для выбора между двумя функциями с двумя аргументами и двумя возвращаемыми значениями; своп аргумента, который возвращает их обоих, но в обратном порядке, и функция тождества, которая возвращает их обоих в том же порядке.
Когда первый аргумент верно, поэтому мы производим отредактированную версию второго аргумента , что свопы аргументов перед запуском, то есть Precompose с «аргументами свопа», то есть not
. Таким образом, истинный первый аргумент означает, что мы возвращаем not
второй аргумент. С другой стороны, ложный первый аргумент означает, что мы сочиняем с функцией тождества, т.е. ничего не делаем. Результатом является реализация xor
.