Golfscript - 12 caratteri
{,1\{)*}/}:f
Introduzione a Golfscript - Factorial passo dopo passo
Ecco qualcosa per le persone che stanno cercando di imparare il golfscript. Il prerequisito è una conoscenza di base di golfscript e la capacità di leggere la documentazione di golfscript.
Quindi vogliamo provare il nostro nuovo strumento golfscript . È sempre bene iniziare con qualcosa di semplice, quindi iniziamo con fattoriale. Ecco un tentativo iniziale, basato su un semplice pseudocodice imperativo:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
Gli spazi bianchi sono usati molto raramente in golfscript. Il trucco più semplice per sbarazzarsi di spazi bianchi è utilizzare nomi di variabili diverse. Ogni token può essere utilizzato come variabile (vedere la pagina della sintassi ). Gettoni utile utilizzare come variabili sono caratteri speciali come |
, &
, ?
- in genere non niente utilizzato in altre parti del codice. Questi vengono sempre analizzati come token a carattere singolo. Al contrario, variabili come n
richiedono uno spazio per spingere un numero nello stack dopo. I numeri sono essenzialmente variabili preinizializzate.
Come sempre, ci saranno dichiarazioni che possiamo cambiare, senza influire sul risultato finale. In golfscript, tutto tranne che restituisce true 0
, []
, ""
, e {}
(si veda questo ). Qui, possiamo cambiare semplicemente la condizione di uscita del loop {n}
(ripetiamo un altro tempo e terminiamo quando n = 0).
Come per il golf in qualsiasi lingua, aiuta a conoscere le funzioni disponibili. Fortunatamente la lista è molto breve per golfscript. Possiamo cambiare 1-
in (
per salvare un altro personaggio. Al momento il codice è simile al seguente: (potremmo usare al 1
posto di |
qui se volessimo, il che lascerebbe cadere l'inizializzazione).
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
È importante utilizzare bene lo stack per ottenere le soluzioni più brevi (pratica pratica pratica). In generale, se i valori vengono utilizzati solo in un piccolo segmento di codice, potrebbe non essere necessario memorizzarli in variabili. Rimuovendo la variabile del prodotto in esecuzione e semplicemente usando lo stack, possiamo salvare molti caratteri.
{:n;1{n}{n*n(:n;}while}:f
Ecco qualcos'altro a cui pensare. Stiamo rimuovendo la variabile n
dalla pila alla fine del corpo del loop, ma poi la spingiamo immediatamente dopo. In effetti, prima che inizi il loop, lo rimuoviamo anche dallo stack. Dovremmo invece lasciarlo nello stack e possiamo mantenere vuota la condizione del loop.
{1\:n{}{n*n(:n}while}:f
Forse possiamo persino eliminare completamente la variabile. Per fare ciò, dovremo mantenere la variabile in pila in ogni momento. Ciò significa che abbiamo bisogno di due copie della variabile in pila alla fine del controllo delle condizioni in modo da non perderle dopo il controllo. Ciò significa che avremo uno ridondante 0
nello stack dopo la fine del ciclo, ma è facile da risolvere.
Questo ci porta alla nostra while
soluzione loop ottimale !
{1\{.}{.@*\(}while;}:f
Ora vogliamo ancora renderlo più breve. L'obiettivo ovvio dovrebbe essere la parola while
. Guardando la documentazione, ci sono due alternative praticabili: spiegare e fare . Quando hai una scelta di percorsi diversi da prendere, prova a valutare i vantaggi di entrambi. Unfold è "praticamente un ciclo while", quindi come stima ridurremo i 5 caratteri while
di 4 in /
. Per quanto riguarda do
, tagliamo while
3 caratteri e arriviamo a unire i due blocchi, il che potrebbe salvare un altro personaggio o due.
In realtà c'è un grosso svantaggio nell'uso di un do
loop. Poiché il controllo delle condizioni viene eseguito dopo che il corpo è stato eseguito una volta, il valore di 0
sarà errato, quindi potrebbe essere necessaria un'istruzione if. Ti dirò ora che unfold è più breve (alcune soluzioni do
sono fornite alla fine). Vai avanti e provalo, il codice che abbiamo già richiede modifiche minime.
{1\{}{.@*\(}/;}:f
Grande! La nostra soluzione ora è super corta e abbiamo finito qui, giusto? No. Sono 17 caratteri e J ha 12 caratteri. Non ammettere mai la sconfitta!
Ora stai pensando con ... ricorsione
L'uso della ricorsione significa che dobbiamo usare una struttura ramificata. Sfortunato, ma dato che il fattoriale può essere espresso in modo così sintetico in modo ricorsivo, questa sembra un'alternativa praticabile all'iterazione.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Beh, è stato facile: se avessimo provato la ricorsione in precedenza, forse non avremmo nemmeno guardato usando un while
loop! Tuttavia, siamo solo a 16 caratteri.
Array
Gli array sono generalmente creati in due modi - utilizzando l' [
e ]
personaggi, o con la ,
funzione di. Se eseguito con un numero intero nella parte superiore dello stack, ,
restituisce un array di quella lunghezza con arr [i] = i.
Per iterare sugli array, abbiamo tre opzioni:
{block}/
: spingere, bloccare, spingere, bloccare, ...
{block}%
: [push, block, push, block, ...] (questo ha alcune sfumature, ad esempio i valori intermedi vengono rimossi dallo stack prima di ogni push)
{block}*
: spingere, spingere, bloccare, spingere, bloccare, ...
La documentazione di golfscript contiene un esempio di {+}*
come sommare il contenuto di un array. Questo suggerisce che possiamo usare {*}*
per ottenere il prodotto di un array.
{,{*}*}:f
Sfortunatamente, non è così semplice. Tutti gli elementi sono disattivati di uno ( [0 1 2]
anziché di [1 2 3]
). Possiamo usare {)}%
per correggere questo problema.
{,{)}%{*}*}:f
Beh non proprio. Questo non gestisce zero correttamente. Possiamo calcolare (n + 1)! / (N + 1) per rettificare questo, sebbene ciò costi troppo.
{).,{)}%{*}*\/}:f
Possiamo anche provare a gestire n = 0 nello stesso bucket di n = 1. In realtà questo è estremamente breve da fare, provare e allenarsi il più breve possibile.
Non così bene è l'ordinamento, a 7 caratteri: [1\]$1=
. Nota che questa tecnica di ordinamento ha scopi utili, come imporre limiti a un numero (es. `[0 \ 100] $ 1 =)
Ecco il vincitore, con solo 3 caratteri:.! +
Se vogliamo avere l'incremento e la moltiplicazione nello stesso blocco, dovremmo scorrere su ogni elemento dell'array. Dal momento che non stiamo costruendo un array, questo significa che dovremmo utilizzare {)*}/
, il che ci porta alla più breve implementazione golfscript di fattoriale! A 12 caratteri, questo è legato a J!
{,1\{)*}/}:f
Soluzioni bonus
A partire da una if
soluzione semplice per un do
ciclo:
{.{1\{.@*\(.}do;}{)}if}:f
Ne possiamo spremere un paio in più. Un po 'complicato, quindi dovrai convincerti che questi funzionano. Assicurati di aver compreso tutti questi.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Un'alternativa migliore è calcolare (n + 1)! / (N + 1), che elimina la necessità di una if
struttura.
{).1\{.@*\(.}do;\/}:f
Ma la do
soluzione più breve qui richiede alcuni caratteri per mappare da 0 a 1 e tutto il resto su se stesso, quindi non abbiamo bisogno di alcuna ramificazione. Questo tipo di ottimizzazione è estremamente facile da perdere.
{.!+1\{.@*\(.}do;}:f
Per chiunque sia interessato, qui sono fornite alcune soluzioni ricorsive alternative con la stessa lunghezza di cui sopra:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* nota: non ho ancora testato molte delle parti di codice in questo post, quindi sentitevi liberi di informare se ci sono errori.