EDIT: Come alcuni di voi sospettavano, c'era un bug nell'interprete ufficiale: l'ordine di composizione in .era invertito. Avevo due versioni dell'interprete e ho usato quella sbagliata qui. Gli esempi sono stati scritti anche per questa versione errata. Ho corretto l'interprete nel repository e gli esempi seguenti. Anche la descrizione >era un po 'ambigua, quindi l'ho risolto. Inoltre, mi scuso per questo tempo, sono stato coinvolto in alcune cose della vita reale.
EDIT2: Il mio interprete aveva un bug nell'implementazione .che si rifletteva negli esempi (si basavano su comportamenti indefiniti). Il problema è stato risolto.
introduzione
Shift è un linguaggio di programmazione funzionale esoterico che ho creato un paio di anni fa, ma pubblicato oggi. È basato sullo stack, ma ha anche il curry automatico come Haskell.
specificazione
Ci sono due tipi di dati in Shift:
- Funzioni che hanno un'arità positiva arbitraria (numero di ingressi) e che restituiscono un elenco di uscite. Ad esempio, una funzione che duplica il suo unico input ha arity 1 e una funzione che scambia i suoi due input ha arity 2.
- Gli spazi vuoti, che sono tutti identici e non hanno altro scopo che non essere funzioni.
Un programma Shift è composto da zero o più comandi , ognuno dei quali è un singolo carattere ASCII. Ci sono 8 comandi in totale:
!( applica ) apre una funzionefe un valorexdallo stack e si applicafax. Sefha arity 1, l'elencof(x)viene aggiunto all'inizio dello stack. Se ha arityn > 1, una nuova(n-1)funzione -arygviene inserita nello stack. Prende input e restituisce .x1,x2,...,xn-1f(x,x1,x2,...,xn-1)?( vuoto ) inserisce uno spazio vuoto nella pila.+( clone ) inserisce nello stack una funzione unaria che duplica il suo input: qualsiasi valorexviene mappato[x,x].>( shift )ninserisce nello stack una funzione unaria che accetta una funzione -aryfe restituisce una(n+1)funzione -arygche ignora il suo primo argomentox, chiamafi rimanenti e puntaxdi fronte al risultato. Ad esempio,shift(clone)è una funzione binaria che accetta inputa,be resi[a,b,b]./( fork ) inserisce nello stack una funzione ternaria che accetta tre inputa,b,ce restituisce[b]seaè vuoto, e in caso[c]contrario.$( Chiamata ) spinge allo stack una funzione binaria che si apre una funzionefe un valorex, e si applicafaxesattamente come!fa..( catena ) spinge nello stack una funzione binaria che apre due funzionifegrestituisce la loro composizione: una funzionehche ha la stessa arità dife che accetta normalmente i suoi input, si applicafa loro e quindi si applica completamentegal risultato (chiamate tutte le volte che la sua arità lo impone), con oggetti inutilizzati dall'output difrimanere nel risultato dih. Ad esempio, supponiamo chefsia una funzione binaria che clona il suo secondo argomento edgè call . Se lo stack contiene[f,g,a,b,c]e lo facciamo.!!, allora contiene[chain(f,g),a,b,c]; se lo facciamo!!dopo,fviene prima applicatoa,b, producendo[a,b,b], quindigviene applicato ai primi due elementi di ciò poiché la sua arity è 2, producendo[a(b),b], e lo stack sarà finalmente[a(b),b,c].@( diciamo ) spinge una funzione unaria che restituisce semplicemente il suo input e stampa0se era uno spazio vuoto e1se era una funzione.
Si noti che tutti i comandi, tranne che !semplicemente spingono un valore nello stack, non c'è modo di eseguire l'input e l'unico modo per produrre qualcosa è usare @. Un programma viene interpretato valutando i comandi uno per uno, stampando 0s o 1s ogniqualvolta "dice" si chiama, e l'uscita. Qualsiasi comportamento non descritto qui (applicazione di uno spazio vuoto, applicazione di uno stack di lunghezza 0 o 1, chiamata "catena" su uno spazio vuoto ecc.) È indefinito: l'interprete potrebbe bloccarsi, fallire silenziosamente, chiedere input o altro.
L'obiettivo
Il tuo compito è scrivere un interprete per Shift. Dovrebbe prendere da STDIN, riga di comando o argomento di funzione un programma Shift da interpretare e stampare su STDOUT o restituire l'output (forse infinito) risultante di 0s e 1s. Se scrivi una funzione, devi essere in grado di accedere agli output a lunghezza infinita in qualche modo (generatore in Python, lista pigra in Haskell, ecc.). In alternativa, puoi prendere un altro input, un numero ne restituire almeno ncaratteri dell'output se è più lungo di n.
Vince il conteggio di byte più basso e non sono consentite scappatoie standard.
Casi test
Questo programma Shift stampa 01:
?@!@@!
A partire da sinistra: spingere uno spazio vuoto, premere dire , quindi applicare la parola allo spazio vuoto. Questo produce 0. Poi, spinta dire due volte, e applicare la seconda parola al primo. Questo produce 1.
Questo programma esegue un ciclo continuo, senza produrre output:
$+.!!+!!
Invia chiamata e clona , quindi applica loro la catena (abbiamo bisogno di due !secondi poiché la catena è una funzione binaria). Ora lo stack contiene una funzione che accetta un argomento, lo duplica e chiama la prima copia sul secondo. Con +!!, dupliciamo questa funzione e la chiamiamo su se stessa.
Questo programma stampa 0010:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
Spingere uno spazio vuoto e dire . Quindi, componi una funzione binaria che copia il suo secondo argomento b, quindi copia il primo ae lo compone con se stesso, quindi applica la composizione alla copia di b, ritornando [a(a(b)),b]. Applicalo per dire e vuoto, quindi applica dire ai due elementi rimanenti nella pila.
Questo programma stampa 0. Per ognuno di !!!quelli che aggiungi, ne stampa un ulteriore 0.
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
Spingere uno spazio vuoto e dire . Quindi, componi una funzione ternaria che accetta f,g,xcome input e restituisce [f,f,g,g(x)]. Clona quella funzione e applicala a se stessa, diciamo , e al vuoto. Questa applicazione non modifica lo stack, quindi possiamo applicare nuovamente la funzione tutte le volte che vogliamo.
Questo programma stampa la sequenza infinita 001011011101111..., dove il numero di 1s aumenta sempre di uno:
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
Il repository contiene una versione annotata.
f(x1, x2, ..., xn)e g(y1, y2, ..., ym). La chiamata .fa scoppiare entrambi e spinge una funzione h(z1, z2, ..., zn). Ora puoi sgretolare tutti questi argomenti valutandoli gradualmente !. Dopo ntali applicazioni, la funzione rimanente aveva solo un argomento, e a quel punto calcola f(z1, z2, ..., zn)(vale fa dire applicata a tutti gli argomenti in cui hai scritto), che invia alcuni nuovi valori e quindi consuma immediatamente i mvalori dallo stack e gli chiama .
.funziona esattamente come descritto da Martin, tranne per il fatto che se frestituisce un elenco inferiore ai mvalori, il risultato è indefinito (la composizione ha arity n, quindi non può mangiare più argomenti dallo stack). In sostanza, l'output di fviene utilizzato come stack temporaneo, sul quale gviene spinto e applicato il mtempo impiegato !, e il risultato viene aggiunto allo stack principale.