Risposte:
*
per l'outputPoiché è possibile eseguire l'output lasciando una stringa nello stack , potrebbe essere utile accumulare la stringa utilizzando *
invece di eseguire l' output con S
. Supponiamo che la tua sfida sia "prendere una stringa e aggiungere uno spazio", il modo per farlo con l'output sarebbe:
S( )S
Il modo per farlo *
, d'altra parte è un byte più breve:
( )*
Il problema è che se l'output ha molti accumuli, potrebbe costare byte gestire l'elemento di output nello stack.
Se devi usare molto un pezzo di codice, ha senso archiviarlo nello stack e duplicarlo e valutarlo di tanto in tanto. Finora, questa è solo la normale programmazione di Underload. Sfortunatamente, mantenere un valore nello stack per lungo tempo è difficile e tende a rendere il codice dettagliato, e questo è vero anche se il valore è una funzione piuttosto che dati. Questo peggiora molto se hai più funzioni che devono essere riutilizzate ripetutamente.
Nel tipo di programma più grande che potrebbe trarre vantaggio da diverse funzioni riutilizzate, una soluzione che puoi utilizzare è invece quella di creare una funzione di grandi dimensioni in grado di adempiere a qualsiasi dei loro scopi a seconda del modo in cui viene chiamata (in base a ciò che è sotto nello stack, o tramite utilizzando più chiamare sequenze che solo ^
; una funzione scritto con cura in grado di distinguere ^^
da ^:^
da^*^
da ^~^
, dandovi quattro distinti, piuttosto brevi sequenze). In questo "dizionario" puoi anche memorizzare altre cose utili, come le stringhe che usi più volte. Nota che se usi il dizionario pesantemente, può avere senso trasformarlo in una specie di quine, spingendo una copia di se stesso nello stack, in modo che non sia necessario copiarlo manualmente con:
essere in grado di usarlo senza perdere la capacità di usarlo in futuro.
^!!!!^
ricerca di stile preferita (che ho anche usato in molti altri esempi sulla pagina, specialmente nella sezione di minimizzazione). Anche se questo potrebbe non dare la ricerca più breve.
Come semplice esempio, l'implementazione più comunemente vista dei booleani è !()
per false (cioè intero 0) e la stringa nulla per vero (cioè intero 1), ma se si riscontra un problema fortemente basato su XOR logico, si potrebbe fare di più ha senso usare la stringa null per false e ~
per true (questo formato di dati può essere convertito in qualsiasi altro formato booleano usando (false)~(true)~^!
e consente un'implementazione molto concisa *
per XOR.
È possibile approfondire ulteriormente questo principio generale e utilizzare le funzioni di cui il programma avrà bisogno in seguito come parte dei valori dei dati; che evita di dover memorizzare le funzioni e i dati separatamente nello stack. Questo può rendere il flusso di controllo un po 'più confuso, ma quando si gioca a golf, la manutenibilità deve spesso passare in secondo piano, e non è comunque possibile che Underload sia tutto ciò utilizzabile.
(!)
e (~!)
per booleani, ma la tua strada sembra migliore.
Il modo funzionale puro di decrementare un numero di Church è di usare la funzione predecessore del calcolo lambda:
\n.n(\p.\z.z($(pT))(pT))(\z.z0[whatever you define the predecessor of 0 to be])
Dove 0 = \ x. \ Yy, T = \ x. \ Yx e $ è il successore.
Riscritto in Underload, si tratta di 28 byte:
(!())~(!())~(!:(:)~*(*)*~)~^!
Questo va bene, ma siamo in grado di sfruttare alcune delle proprietà utili di sottocarico, vale a dire che :!
e ()*
fare sono no-ops. Ciò significa che, per un numero n
, :ⁿ!!()()*ⁿ
(dove cⁿ
si c
ripetono i n
tempi) si ottiene n-1. Ad esempio, facendo questo per il numero 3 della Chiesa si ottiene questo:
:::!!()()***
Rimuovendo le coppie no-op, otteniamo:
:*
Che è 2.
Quindi questa è la nuova e più breve operazione precedente:
:(:)~^(!!()())*~(*)~^*
Si tratta di 7 byte in meno.
(()~(:))~:(^!!())*~(*)~^**
è ancora più corto di 3 byte.
Underload ha in realtà due stack: lo stack di stringhe e lo stack di comandi che compongono il codice sorgente. Le ^
istruzioni di Underload ci consentono di spostare le stringhe dal primo stack al secondo. In questo modo, possiamo risparmiare molte manipolazioni dello stack non necessarie.
Ad esempio, supponiamo di avere (a)(b)(c)
nello stack principale e che vorremmo concatenare i due elementi inferiori, ignorando (c)
, per ottenere (ab)(c)
. Il modo ingenuo per farlo è ruotare la pila per ottenere (c)(a)(b)
e quindi concantenare e scambiare:
a~a~*~a*^*~
Questo non va bene. L'uso a~a~*~a*^
per ruotare la pila in questo modo è estremamente costoso e dovrebbe essere evitato quando possibile. Mettendo (c)
invece nello spazio del programma, questo può essere ridotto di quattro byte:
a(*)~*^
L'idea è di prendere le istruzioni che si desidera eseguire e quindi aggiungere un'istruzione per respingere (c)
alla fine, quindi valutare il risultato. Ciò significa che non dobbiamo preoccuparci (c)
fino a quando non viene respinto dopo che abbiamo finito.
(*)~a*^
, che penso sia un po 'più compostabile. Essenzialmente ~a*^
è il dip
comando di Gioia.
eval
comando, non ho mai visto un linguaggio come quello prima.