(~!)(!)((~)~*):((!)~^)*(:^)(~(!)~^(~)~*)(()~(~)~^~*)
Experimente online! (inclui um conjunto de testes e um texto que identifica partes do programa)
Isso é surpreendentemente bom para um esolang de nível muito baixo. (Numerais da igreja, booleanos da igreja etc. são muito comumente usados no Underload por esse motivo; o idioma não possui números e booleanos incorporados, e essa é uma das maneiras mais fáceis de simulá-los. Dito isso, também é comum codificar booleanos como os números 0 e 1. da Igreja)
Para quem está confuso: Underload permite definir funções reutilizáveis, mas não permite que você as nomeie da maneira normal, elas meio que flutuam na pilha de argumentos (por isso, se você definir cinco funções e depois chamar a primeira) você definiu, precisa escrever uma nova função que use cinco argumentos e chame o quinto deles, depois chame-a com argumentos insuficientemente para que procure argumentos extras a serem usados). Chamá-los destrói-os por padrão, mas você pode modificar a chamada para torná-la não destrutiva (em casos simples, você só precisa adicionar dois pontos à chamada, embora os casos complexos sejam mais comuns porque você precisa garantir que as cópias na pilha não atrapalha), então o suporte às funções do Underload tem todos os requisitos que precisamos da pergunta.
Explicação
verdade
(~!)
( ) Define function:
~ Swap arguments
! Delete new first argument (original second argument)
Essa é bem direta; nos livramos do argumento que não queremos e o argumento que queremos permanece lá, servindo como valor de retorno.
falso
(!)
( ) Define function:
! Delete first argument
Este é ainda mais direto.
não
((~)~*)
( ) Define function:
~* Modify first argument by pre-composing it with:
(~) Swap arguments
É divertido: not
não chama de argumento, apenas usa uma composição de função. Esse é um truque comum no Underload, no qual você não inspeciona seus dados, apenas altera a forma como eles funcionam pré e pós-compondo as coisas com ele. Nesse caso, modificamos a função para trocar seus argumentos antes da execução, o que claramente nega um numeral da Igreja.
e
:((!)~^)*
( ) 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
A questão permite definir funções em termos de outras funções. Definimos "e" a seguir, porque quanto mais recentemente "não" tiver sido definido, mais fácil será usá-lo. (Isso não subtrai nossa pontuação, porque não estamos nomeando "não", mas economiza bytes ao escrever a definição novamente. Essa é a única vez que uma função se refere a outra, porque se refere a qualquer função mas o definido mais recentemente custaria muitos bytes.)
A definição aqui é and x y = (not x) false y
. Em outras palavras, se not x
retornarmos false
; caso contrário, retornamos y
.
ou
(:^)
( ) Define function:
: Copy the first argument
^ Execute the copy, with arguments
{implicitly, the original first argument}
{and implicitly, our second argument}
O @Nitrodon apontou nos comentários que or x y = x x y
normalmente são menores que or x y = x true y
e que também estão corretos no Underload. Uma implementação ingênua disso seria(:~^)
, mas podemos obter um byte adicional observando que não importa se executamos o primeiro argumento original ou a cópia dele, o resultado é o mesmo.
Underload não suporta curry no sentido usual, mas definições como essa fazem com que pareça! (O truque é que argumentos não consumidos permanecem por aí, então a função que você chama os interpretará como seus próprios 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
A definição usada aqui é implies x y = not (y false x)
. Se y for verdadeiro, isso simplifica para not false
, ie true
. Se y for falso, isso simplifica anot x
, fornecendo a tabela de verdade que queremos.
Nesse caso, estamos usando not
novamente, desta vez reescrevendo seu código em vez de fazer referência a ele. É apenas escrito diretamente como(~)~*
sem parênteses, portanto é chamado e não definido.
xor
(()~(~)~^~*)
( ) Define function:
~ ~^ Execute the first argument, with arguments:
(~) "swap arguments"
() identity function
~* Precompose the second argument with {the result}
Desta vez, estamos avaliando apenas um dos nossos dois argumentos e usá-lo para determinar o que compor no segundo argumento. Underload permite que você jogue rápido e livre com aridade, então estamos usando o primeiro argumento para escolher entre duas funções de dois argumentos e dois retornos; a troca de argumentos que retorna os dois, mas na ordem oposta, e a função de identidade que retorna os dois na mesma ordem.
Quando o primeiro argumento é verdadeiro, produzimos uma versão editada do segundo argumento que troca seus argumentos antes da execução, ou seja, pré-compõe com "argumentos de troca", ou seja not
. Portanto, um verdadeiro primeiro argumento significa que retornamos not
o segundo argumento. Por outro lado, um primeiro argumento falso significa que compomos com a função de identidade, ou seja, não fazemos nada. O resultado é uma implementação de xor
.