(~!)(!)((~)~*):((!)~^)*(:^)(~(!)~^(~)~*)(()~(~)~^~*)
Provalo online! (include una suite di test e parti identificative testuali del programma)
Questo punteggio sorprendentemente bene per un esolang di livello molto basso. (I numeri della chiesa, i booleani della Chiesa, ecc. Sono molto comunemente usati in Underload per questo motivo; la lingua non ha numeri e booleani incorporati, e questo è uno dei modi più semplici per simularli. Detto questo, è anche comune a codificare i booleani come i numeri della Chiesa 0 e 1.)
Per chiunque sia confuso: Underload ti consente di definire funzioni riutilizzabili, ma non ti consente di nominarle in modo normale, semplicemente galleggiano nello stack di argomenti (quindi se definisci cinque funzioni e vuoi chiamare la prima hai definito, devi scrivere una nuova funzione che accetta cinque argomenti e li chiama il quinto, quindi chiamalo con un numero insufficiente di argomenti in modo che cerchi argomenti di riserva da usare). Chiamarli li distrugge per impostazione predefinita, ma è possibile modificare la chiamata per renderla non distruttiva (in casi semplici, è sufficiente aggiungere due punti alla chiamata, anche se i casi complessi sono più comuni perché è necessario assicurarsi che le copie sullo stack non ti ostacolano), quindi il supporto delle funzioni di Underload ha tutti i requisiti di cui avremmo bisogno dalla domanda.
Spiegazione
vero
(~!)
( ) Define function:
~ Swap arguments
! Delete new first argument (original second argument)
Questo è abbastanza semplice; ci liberiamo dell'argomento che non vogliamo e l'argomento che vogliamo rimane lì, fungendo da valore di ritorno.
falso
(!)
( ) Define function:
! Delete first argument
Questo è ancora più semplice.
non
((~)~*)
( ) Define function:
~* Modify first argument by pre-composing it with:
(~) Swap arguments
Questo è divertente: not
non chiama affatto il suo argomento, usa solo una composizione di funzioni. Questo è un trucco comune in Underload, in cui non si ispeziona affatto i dati, si cambia semplicemente il modo in cui funziona pre e post-composizione delle cose con esso. In questo caso, modifichiamo la funzione per scambiarne gli argomenti prima di eseguire, il che nega chiaramente un numero della Chiesa.
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
La domanda consente di definire le funzioni in termini di altre funzioni. Definiamo "e" in seguito perché più recentemente è stato definito "non", più è facile utilizzarlo. (Questo non sottrae dal nostro punteggio, perché non stiamo affatto nominando "non", ma salva i byte nel scrivere nuovamente la definizione. Questa è l'unica volta in cui una funzione fa riferimento a un'altra, perché si riferisce a qualsiasi funzione ma l'ultimo definito costerebbe troppi byte.)
La definizione qui è and x y = (not x) false y
. In altre parole, se not x
, allora ritorniamo false
; altrimenti, torniamo y
.
o
(:^)
( ) Define function:
: Copy the first argument
^ Execute the copy, with arguments
{implicitly, the original first argument}
{and implicitly, our second argument}
@Nitrodon ha sottolineato nei commenti che or x y = x x y
è normalmente più breve di or x y = x true y
e che risulta essere corretto anche in Underload. Un'implementazione ingenua di ciò sarebbe (:~^)
, ma possiamo golfare un byte aggiuntivo notando che non importa se eseguiamo il primo argomento originale o la sua copia, il risultato è lo stesso in entrambi i casi.
Underload in realtà non supporta il curry nel solito senso, ma definizioni come questa lo fanno sembrare così! (Il trucco è che gli argomenti non consumati restano fermi, quindi la funzione chiamata li interpreterà come propri argomenti.)
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 definizione usata qui è implies x y = not (y false x)
. Se y è vero, questo semplifica not false
, ad es true
. Se y è falso, questo si semplifica not x
, dandoci così la tabella della verità che vogliamo.
In questo caso, stiamo usando di not
nuovo, questa volta riscrivendo il suo codice anziché fare riferimento a esso. È solo scritto direttamente come (~)~*
senza parentesi, quindi viene chiamato anziché definito.
xor
(()~(~)~^~*)
( ) Define function:
~ ~^ Execute the first argument, with arguments:
(~) "swap arguments"
() identity function
~* Precompose the second argument with {the result}
Questa volta, stiamo valutando solo uno dei nostri due argomenti e lo stiamo usando per determinare cosa comporre sul secondo argomento. Underload ti permette di giocare veloce e sciolto con arity, quindi stiamo usando il primo argomento per scegliere tra due funzioni a due argomenti a due ritorni; l'argomento swap che li restituisce entrambi ma nell'ordine opposto e la funzione di identità che li restituisce entrambi nello stesso ordine.
Quando il primo argomento è vero, produciamo quindi una versione modificata del secondo argomento che scambia i suoi argomenti prima dell'esecuzione, ovvero precompone con "argomenti di scambio", vale a dire not
. Quindi un vero primo argomento significa che restituiamo not
il secondo argomento. D'altra parte, un primo argomento falso significa che componiamo con la funzione identità, cioè non facciamo nulla. Il risultato è un'implementazione di xor
.