(~!)(!)((~)~*):((!)~^)*(:^)(~(!)~^(~)~*)(()~(~)~^~*)
Pruébalo en línea! (incluye un traje de prueba y texto que identifica partes del programa)
Esto es sorprendentemente bueno para un esolang de muy bajo nivel. (Los números de iglesia, los booleanos de la iglesia, etc. se usan muy comúnmente en Underload por esta razón; el lenguaje no tiene números y booleanos integrados, y esta es una de las formas más fáciles de simularlos. Dicho esto, también es común codificar booleanos como los números de la Iglesia 0 y 1.)
Para cualquiera que esté confundido: Underload le permite definir funciones reutilizables, pero no le permite nombrarlas de la manera normal, simplemente flotan en la pila de argumentos (por lo tanto, si define cinco funciones y luego desea llamar a la primera) usted definió, necesita escribir una nueva función que tome cinco argumentos y llame al quinto de ellos, luego llámela con suficientes argumentos para que busque argumentos de repuesto para usar). Llamarlos los destruye de manera predeterminada, pero puede modificar la llamada para que no sea destructiva (en casos simples, solo necesita agregar dos puntos a la llamada, aunque los casos complejos son más comunes porque necesita asegurarse de que las copias en la pila no se interponga en su camino), por lo que el soporte de funciones de Underload tiene todos los requisitos que necesitaríamos de la pregunta.
Explicación
cierto
(~!)
( ) Define function:
~ Swap arguments
! Delete new first argument (original second argument)
Este es bastante sencillo; nos deshacemos del argumento que no queremos y el argumento que queremos simplemente permanece allí, sirviendo como valor de retorno.
falso
(!)
( ) Define function:
! Delete first argument
Este es aún más sencillo.
no
((~)~*)
( ) Define function:
~* Modify first argument by pre-composing it with:
(~) Swap arguments
Esta es la diversión: not
no llama a su argumento en absoluto, solo usa una composición de función. Este es un truco común en Underload, en el que no inspeccionas tus datos en absoluto, solo cambias su funcionamiento al pre y post componer cosas con ellos. En este caso, modificamos la función para intercambiar sus argumentos antes de ejecutar, lo que claramente niega un número de Iglesia.
y
:((!)~^)*
( ) 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
La pregunta permite definir funciones en términos de otras funciones. Definimos "y" siguiente porque cuanto más recientemente se ha definido "no", más fácil es usarlo. (Esto no resta de nuestro puntaje, porque no estamos nombrando "no" en absoluto, pero ahorra bytes al escribir la definición nuevamente. Esta es la única vez que una función se refiere a otra, porque se refiere a cualquier función pero la definición más reciente costaría demasiados bytes).
La definición aquí es and x y = (not x) false y
. En otras palabras, si not x
, entonces volvemos false
; de lo contrario, volvemos y
.
o
(:^)
( ) Define function:
: Copy the first argument
^ Execute the copy, with arguments
{implicitly, the original first argument}
{and implicitly, our second argument}
@Nitrodon señaló en los comentarios que or x y = x x y
normalmente es más corto que or x y = x true y
, y que resulta ser correcto en Underload también. Sería una implementación ingenua de eso (:~^)
, pero podemos aprovechar un byte adicional al notar que no importa si ejecutamos el primer argumento original o la copia del mismo, el resultado es el mismo de cualquier manera.
Underload en realidad no admite curry en el sentido habitual, ¡pero definiciones como esta hacen que parezca que sí! (El truco es que los argumentos no consumidos simplemente se quedan, por lo que la función que llama los interpretará como sus propios argumentos).
implica
(~(!)~^(~)~*)
( ) Define function:
~ Swap arguments
~^ Execute the new first (original second) argument, with argument:
(!) false
{and implicitly, our second argument}
(~)~* Run "not" on the result
La definición utilizada aquí es implies x y = not (y false x)
. Si y es verdadero, esto se simplifica a not false
, es decir true
. Si y es falso, esto se simplifica a not x
, lo que nos da la tabla de verdad que queremos.
En este caso, lo estamos utilizando not
nuevamente, esta vez reescribiendo su código en lugar de hacer referencia a él. Simplemente se escribe directamente (~)~*
sin paréntesis, por lo que se llama en lugar de definirse.
xor
(()~(~)~^~*)
( ) Define function:
~ ~^ Execute the first argument, with arguments:
(~) "swap arguments"
() identity function
~* Precompose the second argument with {the result}
Esta vez, estamos evaluando solo uno de nuestros dos argumentos, y usándolo para determinar qué componer en el segundo argumento. Underload te permite jugar rápido y suelto con arity, por lo que estamos usando el primer argumento para elegir entre dos funciones de dos argumentos y dos retornos; el intercambio de argumentos que los devuelve a ambos pero en el orden opuesto, y la función de identidad que los devuelve a ambos en el mismo orden.
Cuando el primer argumento es verdadero, por lo tanto, producimos una versión editada del segundo argumento que intercambia sus argumentos antes de ejecutar, es decir, precompone con "argumentos de intercambio", es decir not
. Entonces, un primer argumento verdadero significa que devolvemos not
el segundo argumento. Por otro lado, un primer argumento falso significa que componimos con la función de identidad, es decir, no hacemos nada. El resultado es una implementación de xor
.