Verifica del programma Brainfuck


17

Ancora un altro problema di analisi Brainfuck, ma questa volta ... diverso.

Stai lavorando nell'Infinite Monkeys Incorporated, la società che realizza programmi Brainfuck, per risolvere vari problemi interessanti (per caso, non meno - dopo tutto, la società fa programmi casuali). Tuttavia, sembra che le tue macchine Fast Turing che eseguono solo Brainfuck abbiano un piccolo e costoso problema con errori di sintassi: creane uno e il computer esplode. Probabilmente è un difetto di progettazione, ma nessuno si è preso la briga di scoprire perché ciò accada.

Dato che le macchine di Turing (specialmente quelle veloci) sono costose (dopo tutto, hanno una RAM infinita che costa), sarebbe meglio assicurarsi che il programma non abbia errori di sintassi prima di eseguire il codice. La tua azienda eseguirà un sacco di codice, quindi la verifica manuale non funzionerà. Scrivere un programma che legge STDIN per il codice Brainfuck ed esce con lo stato di uscita impostato su un valore diverso da 0 (errore) se il programma presenta un errore di sintassi (ad esempio, ]è un errore di sintassi, perché non è presente alcuna corrispondenza[ ). Esci con lo stato di uscita impostato su 0 se il programma è completamente corretto.

Assicurarsi che il programma rilevi correttamente gli errori che coinvolgono []. Non vorresti che un altro computer esplodesse, vero? Oh, e assicurati che sia il più breve possibile - il tuo capo paga per programmi brevi (perché pensa che siano veloci o qualcosa del genere). Oh, e non devi scrivere codice in Brainfuck (in effetti, non puoi, perché Brainfuck non supporta i codici di uscita) - il tuo codice verrà eseguito su un normale computer.

Quindi, come puoi vedere, il tuo compito è controllare se il programma Brainfuck è "valido" (ha []simboli associati ). Nota che i programmi Brainfuck possono avere caratteri diversi da [], quindi non rifiutare il programma solo perché ha altri comandi. Vince il codice più piccolo, ma probabilmente ti interesserà di più i voti positivi.


1
Che dire delle lingue che non consentono l'impostazione di un codice di uscita del processo?
Kendall Frey,

1
@KendallFrey: semplice, usa qualcos'altro. O in GolfScript, incorporare il codice Ruby. Forse questo blocca alcuni linguaggi di programmazione esoterici, ma bene ...
Konrad Borowski il

1
Va bene impostare il codice di errore su un valore diverso da 1 (purché non sia ovviamente 0) in caso di errore?
Marin

@marinus: non so perché lo vorresti, ma penso che vada bene.
Konrad Borowski il

@GlitchMr: perché allora posso usare al GCD(a,b)posto di 0 != a || b.
Marin

Risposte:


6

GolfScript, 18 caratteri

.{[]`?)},~](`91?./

Questo codice viene eseguito correttamente con il codice di uscita 0 (e stampa alcuni rifiuti su stdout) se le parentesi quadre nell'input sono bilanciate. In caso contrario, non riesce con un codice di uscita diverso da zero e stampa un messaggio di errore su stderr, ad esempio:

(eval):2:in `/': divided by 0 (ZeroDivisionError)

o

(eval):1:in `initialize': undefined method `ginspect' for nil:NilClass (NoMethodError)

Dal momento che la sfida non ha detto nulla sull'output di stdout / stderr, immagino che questo sia valido. In ogni caso, puoi sempre reindirizzare quelli a/dev/null .

Spiegazione:

Il codice {[]`?)},rimuove tutto tranne le parentesi quadre dall'input e il~ valuta il risultato come codice GolfScript. La parte difficile è che le parentesi sbilanciate sono perfettamente legali in GolfScript (e, in effetti, il mio codice ne include uno!), Quindi abbiamo bisogno di un altro modo per bloccare il codice.

Il trucco che sto usando è quello di lasciare una copia dell'input nella parte inferiore dello stack, raccogliere l'intero stack in un array (usando lo sbilanciato ]) e spostare il primo elemento. A questo punto, possono succedere tre cose:

  • Se l'input termina in un non chiuso [, il tentativo di spostare un elemento fuori da un array vuoto provoca l'interruzione dell'interprete (che, in questo caso, è esattamente quello che vogliamo!)
  • Se le parentesi nell'input erano bilanciate, l'elemento spostato sarà la stringa di input originale.
  • Altrimenti (se l'input aveva un non aperto ]o un non chiuso [) sarà un array.

La mia voce originale di 14 caratteri ha quindi confrontato il valore spostato con una stringa, che si arresterebbe in modo anomalo se fosse un array nidificato. Sfortunatamente, si scopre che il confronto di un array flat (o, in particolare, vuoto) con una stringa è anche legale in GolfScript, quindi ho dovuto cambiare virata.

La mia attuale presentazione, invece, usa un metodo molto bruto per distinguere le matrici dalle stringhe: le annulla e cerca di trovare la prima occorrenza di [(codice ASCII 91), che sarà zero se e solo se le non valutate la variabile era un array. In tal caso, dividendo lo zero con se stesso si innesca l'incidente desiderato.

Ps. Altre due soluzioni da 18 caratteri sono:

.{[]`?)},{.}%~](n<

e

.{[]`?)},~]([n]+n<

Purtroppo, non ho ancora trovato un modo più breve per risolvere il "problema di array vuoto".


è bilanciato ?: ][(cioè non fallisce nel tuo programma?)
Justin

@Quincunx: fallisce, come dovrebbe.
Ilmari Karonen,

Tuttavia, si è scoperto di accettare [[]; L'ho risolto ora, al costo di 4 caratteri, e ora supera tutti i miei test.
Ilmari Karonen il

Se ti capisco correttamente, hai avuto una soluzione a 14 caratteri che non riesce a distinguere tra una stringa e una matrice solo nel caso in cui la matrice sia vuota. 1+trasformerà un array vuoto in un array non vuoto, un array non vuoto in un array non vuoto e una stringa in una stringa.
Peter Taylor,

@PeterTaylor: Sì, lo era .{[]`?)},~](n<. Ho provato il tuo 1+, ma sembra che l'array debba contenere qualcosa di diverso da un numero (presumibilmente in modo che l'interprete si arresti in modo anomalo quando tenta di confrontare ricorsivamente un carattere con un array / stringa). Anche l'utilizzo n+non funziona, poiché costringe l'array a una stringa; [n]+ fa il lavoro, ma ancora mi mette a 18 caratteri.
Ilmari Karonen,

17

Brainfuck 76 byte

>>+[<+++++++++[->---------<]+>-[--[[-]<->]<[<[->-]>[<+]<]>]<[-<+>]+>,]<<[<+]

Questo esce dai suoi legami se le parentesi quadre sono sbilanciate trasformando l'interprete / il compilatore bf in modo che fallisca il runtime e alcuni di essi hanno codici di uscita per riflettere ciò.

richiede eof = 0, valori di wrapping e numero finito di celle

In Ubuntu puoi usare l'interprete bf( sudo apt-get install bf)

% echo '[[[]' | bf -n bf_matching.bf
Error: Out of range! Youwanted to '<' below the first cell.
To solve, add some '>'s at the beginning, for example.
an error occured
% echo $? 
5
% bf -n bf_matching.bf < bf_matching.bf
% echo $?
0

5
Mi piace come hai usato Brainfuck anche se la domanda diceva che era impossibile.
Riking

Oh wow. Sono sinceramente sorpreso. Ho pensato che fosse impossibile, ma non lo è.
Konrad Borowski il

1
@xfix: Brainfuck è completamente completo, quindi può fare qualsiasi cosa (dati i vincoli I / O.)
marinus

2
La completezza di @marinus Turing significa solo che può fare tutti i calcoli. BrainfuckTuring è completo senza I / O in quanto è possibile eseguire un programma che calcola qualsiasi calcolo e vedere il risultato controllando la sua memoria dopo l'esecuzione. BF senza I / O renderebbe le persone meno interessate in quanto sarebbe difficile creare utilità. ad es. non avrei mai potuto creare il mio interprete lisp .
Sylwester,

14

Befunge 98 - 26 31 20 19 caratteri

~:']-!\'[-!-+:0`j#q

Mi sono sbarazzato di alcuni enormi condizionali. Ora il programma funziona come segue:

~:   read an input char and duplicate it

']-! push a 1 if the char is the ] char, 0 otherwise

\    swap the comparison value for the duplicated input char

'[-! push a 1 if the char is the [ char, 0 otherwise

-    subtract the two comparison values. If the second one is 1, the first is 0, 
     so the result is -1. Else if the second one is 0, the first is 1 and the result is
     1. Else, the first and the second are both 0 and the result is 0

+    Add the number to the counter (which is 0 if there is none already)

:0`  test if the counter is positive (that'd mean an invalid program). Pushes a 1 if 
     positive, 0 otherwise.

j#q  if the counter is positive, then this jumps to the q. Otherwise, it skips the q 
     and continues to the ~ at the beginning of the line. If there are no more chars in
     the input, then the IP will be sent back to the q. 

qchiude il programma e visualizza il valore più alto come valore di errore. Il valore di errore sarà -1 1 se ce ne sono troppi ], 0 se sono bilanciati e positivo negativo se ce ne sono troppi [. Se il numero è positivo negativo, allora quel numero è il valore assoluto di quel numero] per bilanciare il programma.

Modifica: incremento e decremento commutati. [usato per incrementare il contatore e ]usato per diminuirlo. Commutandolo, risparmio 1 carattere, perché per la condizione di uscita, devo solo verificare se il contatore è positivo, piuttosto che negativo.


Vecchia versione

~:'[-!j;\1+\#;']-!j;1-#;:0\`j#q

Questo codice funziona come segue:

~    read one char of input
:    duplicate it
'[-! push 1 if the character is [, 0 otherwise
j    jump that number of characters
;    skip until next ;
\1+\ increment counter

similarily for ].

#q when end of input is reached, ~ reflects the IP back. q ends the program with the error value on the stack.

Modifica: capito che questo ha accettato input come ][, ora termina ogni volta che il conteggio diventa negativo, usando

:0\`j


@JoKing è abbastanza carino, usando la distanza tra [e ]per fare il confronto con entrambi.
Justin

6

J ( 38 35)

exit({:+.<./)+/\+/1 _1*'[]'=/1!:1[3

Spiegazione:

  • 1!:1[3: leggi stdin
  • '[]'=/: crea una matrice in cui la prima riga è una maschera di bit della [s nell'input e la seconda riga è la] s.
  • 1 _1*: moltiplica la prima riga per 1 e la seconda riga per -1.
  • +/: somma le colonne della matrice insieme, dando delta-rientro per carattere
  • +/\: crea un totale parziale di questi, dando il livello di rientro a ciascun personaggio
  • ({:+.<./): restituisce il GCD dell'elemento finale ( {:) e dell'elemento più piccolo ( <./). Se tutte le parentesi graffe corrispondono, entrambi dovrebbero essere 0così questo tornerà0 . Se le parentesi graffe non corrispondono, verrà restituito un valore diverso da zero.
  • exit: imposta il valore di uscita su quello ed esce.

Bella ripartizione ...
Chris Cashwell,

6

rubino (64)

exit $<.read.tr('^[]','').tap{|x|0while x.gsub!('[]','')}.empty?

precedentemente (68) era:

exit ARGF.read.tr('^[]','').tap{|x| 0 while x.gsub!('[]','')}.empty?

Un'altra soluzione equivalente utilizza

exit $<.read.tr('^[]','').tap{|x|0while x.gsub!('[]','')}.size>0

non può essere usato sizeda solo perché darebbe falsi negativi (!!!) quando il numero totale di parentesi sbilanciate è multiplo di 256


Questo è codegolf. Sono sicuro che puoi rimuovere alcuni spazi bianchi qui. Il rubino è un po 'sensibile agli spazi bianchi, ma in molti casi lo ignora. Per cominciare, non hai bisogno di spazi bianchi vicino =all'operatore.
Konrad Borowski il

versione migliore (in qualche modo ispirata alla soluzione sed + tr). Non sono sicuro di poter ottenere molto meglio di così. Almeno ha solo 4 spazi bianchi!
riscritto il

1
Eppure, posso rimuoverne due.
Konrad Borowski il

2
Penso che gli spazi intorno al 0possono andare.
Marin

fantastico trucco spaziale, grazie!
riscritto il

5

Perl, 30 caratteri

La tua regex ricorsiva di base in soccorso:

perl -0ne 'exit!/^(([^][]|\[(?1)\])*)$/'

Per coloro che non hanno familiarità con gli argomenti della riga di comando utilizzati qui: -0consente di impostare il carattere di fine riga ai fini dell'input del file; l'utilizzo -0senza argomento imposta il carattere di fine riga su EOF. -nlegge automaticamente l'input (in questo caso, l'intero file) in $_anticipo.

Se regex corrisponde, restituisce un valore vero, che viene negato a 0 per il codice di uscita. In caso contrario, il valore di ritorno falso produce un codice di uscita pari a 1.


Immagino che dovrò golf la mia risposta meglio per battere questa soluzione da 30 caratteri.
Justin

Là. La mia soluzione ora è molto più breve di prima. Bella soluzione. +1
Justin,

4

bash (tr + sed) - 42

[ -z `tr -Cd []|sed ":a;s/\[\]//g;t a"` ]

Se non ti dispiace i messaggi di errore, puoi rimuovere l'ultimo spazio tra `e ]per ottenere la lunghezza 41.


A meno che non mi manchi nulla, hai un uso inutile di cate $()( "[]"può anche essere scritto come []). Ho accettato questa risposta, ma fino a quando non vedrò miglioramenti in termini di lunghezza, non ho intenzione di votare questo, dato che a breve, potrebbe essere molto più breve per bash.
Konrad Borowski il

Beh, in realtà non conosco bene gli script di shell. Non riesco a far funzionare gran parte di questi. Ho sostituito $()con backtick e ho fatto il "[]"-> che []hai suggerito.
shiona,


1
Avrei potuto usare la spada, ho provato e fallito, ma mi sbagliavo.
Shiona,

3

Perl (56 caratteri)

$/=0;$r=qr/[^][]*(\[(??{$r})\])*[^][]*/;<>=~m/^$r$/||die

L'ovvia soluzione Perl: una regex ricorsiva. Purtroppo è un costrutto piuttosto prolisso.


3

Haskell (143)

import System.Exit
f=exitFailure
v c []|c==0=exitSuccess|True=f
v c (s:t)|c<0=f|s=='['=v(c+1)t|s==']'=v(c-1)t|True=v c t
main=getContents>>=v 0

to jgon: l'utilizzo di 2 guardie sembra essere più denso di if-then-else. Inoltre, non credo che i tuoi controlli che le parentesi siano nell'ordine giusto ("] [" passa)


oh wow, ho visto questo commento solo quando è stato sottolineato oggi. Risolto il mio codice, ma sì, le guardie sembrano essere più dense (+1).
Jgon

3

C, 73 64 caratteri

Grazie ai suggerimenti di breadbox (anche se questo probabilmente richiede little-endian per funzionare):

i;main(c){while(read(0,&c,1)*(i+1))i+=(c==91)-(c==93);return i;}

Come funziona:

  • implicito int globale i viene inizializzato su 0
  • c diventa un int implicito che diventa argc (inizializzato su 1 ma non ci interessa, basta che i bit più alti non siano impostati)
  • read(0,&c,1)legge un singolo carattere nel byte basso di c (su architetture little-endian) e restituisce 0 su EOF; i+1 != 0a meno che la virgoletta con parentesi bilanciate non vada a -1; moltiplicarli insieme funziona come un (sicuro) booleano E che richiede un carattere in meno di&&
  • c==91valuta 1 per '['e c==93valuta 1 per']' . (Potrebbe esserci qualche trucco complicato che sarebbe più piccolo, ma non riesco a pensare a niente.)
  • return iesce con il codice di stato 0 se è bilanciato, diverso da zero se non lo è. Restituire -1 viola tecnicamente POSIX ma a nessuno importa davvero.

Versione precedente:

main(i){char c;i=0;while(read(0,&c,1)*(i+1))i+=(c==91)-(c==93);return i;}

L'uso di getchar()invece di read accorcia il codice e ti consente di usare (implicito) intinvece che charper la tua variabile. Ricorda inoltre che i globuli vengono automaticamente inizializzati a zero.
breadbox

Grazie, mi sono dimenticato di getchar (). Non sono sicuro di come l'init globale aiuti, tuttavia: la definizione di un int globale richiede più caratteri rispetto all'utilizzo dell'argit implicito.
soffice

1
Anche i globuli possono essere impliciti. Al di fuori di una funzione c;definisce un int globale.
breadbox

Nel frattempo, getchar (c) finisce per non renderlo più breve, almeno usando questo approccio, perché deve essere testato per> = 0 in qualche modo, e un int implicito per c passato in read () non è garantito per lavoro a causa di endianness.
soffice

Che dire ][? Sono sicuro che non è equilibrato. Non devi tenere traccia se diventa negativo almeno una volta?
vsz

2

Lua, 56

os.exit(("["..io.read"*a".."]"):match"^%b[]$"and 0 or 1)

"^%b[]$"cos'è questo? Puoi spiegare? Sicuramente, questo non è regex?
Cruncher,

@ Cruncher: non lo è. È il modello Lua, non regex (i modelli Lua sono simili alle regex, ma non lo sono). In questo caso, corrisponde all'inizio di string ( ^), insieme bilanciato di [e ]con qualsiasi cosa tra ( %b[]) e fine di string ( $).
Konrad Borowski il

1

GTB , 55

0→O:0→X[`I@I="[":X+1→X@I="]":X-1→X@X<0:1→O@!!Ig;e]l;e~O

Misses []

Utilizzare 0per interrompere.


1

MATHEMATICA, 88 caratteri

s = "[ [ my crazy code ] [ don't explode please! [] [ :)Ahh) ]  [] []]]";

r=StringReplace;
StringLength@FixedPoint[r[#,"[]"-> ""]&,r[s,Except@Characters["[]"]-> ""]]

Con la funzione s name like RegularExpression` e StringLengthcon Mathematica non riesco mai a vincere il contesto golf del codice di testo! :)
Murta

So cosa intendi :) ToCharacterCodeè molto più lungo di ordcosì ... PS: Che ne dici di impostare un codice di uscita?
Ajasja,

Length[StringCases[s,"["|"]"]//.{x___,"[","]",y___}:>{x,y}]
alephalpha,

1

Rubino, 59 58

Cerca la parentesi aperta e chiusa, contando [come 1 e ]come -1 ed esce se il conteggio scende sotto 0.

b=0
$<.read.scan(/\]|\[/){exit 1if(b+=92-$&.ord)<0}
exit b

1
Eseguito l'upgrade e accorciato di un personaggio (ho rimosso gli spazi bianchi dopo exit 1averlo chiesto).
Konrad Borowski il

1

Hassium , 104 byte

func main(){l=0;r=0;b=input();foreach (c in b){if(c=="[")l++;if(c=="]")r++;}if(!(r==l))exit(1);exit(0);}

Espanso completo (la nota non funziona nell'interprete online poiché input () è disabilitato) qui


1

Codice di Turing Machine, 286 276 byte

Ancora una volta, sto usando la sintassi della tabella delle regole qui definita .

0 * * l 1
1 _ ! r Q
5 _ _ r 5
5 * * * W
W [ ! l E
W ] ! l T
W _ _ l I
W * ! r *
E ! ! l *
E * * * R
R _ [ r O
R * * l *
T ! ! l *
T * * * Y
Y _ _ r U
Y * * l *
U [ _ r O
U ! ! * halt-err
I ! ! l *
I _ _ r halt
I * * l halt-err
O ! ! r Q
O _ _ l I
O * * r *
Q ! ! r *
Q * * * W

Termina in stato haltper accettare l'input e halt-errrifiutarlo.


halt-errpuò essere più breve, come halt*ad esempio.
Erik the Outgolfer,

1

Pyth, 25 byte

Ilzv::z"[^[\]]"k"]\[""],[

Provalo online!

Traduzione di Python 3:
import re
z=input()
if len(z):
 print(eval(re.sub("]\[","],[",re.sub("[^[\]]","",z))))

1

Haskell ( 167 , 159)

L'ho fatto principalmente per divertimento, se qualcuno ha qualche suggerimento su come abbreviarlo, sarei felice di ascoltarli anche se :)

import System.Exit
p x y b|b=x|True=y
main=getContents>>=(return.all (>=0).scanl (\v->p (v-1) (v+1).(==']')) 0.filter (`elem`"[]"))>>=p exitSuccess exitFailure

Modifica: risolto un problema segnalato nei commenti (aggiunti 11 byte).

Modifica 2: Creazione di una funzione ausiliaria per il test dei predicati usando protezioni ispirate all'utente 1350, rimuovendo 8 byte.



@ user202729 Questo è un punto eccellente, che è stato superconsiderato. Abbastanza sicuro che sia stato risolto ora.
Jgon

1

Stax , 14 11 caratteri

╬Äτ↔EªÉs «Ü

Esegui ed esegui il debug

Accredita a @recursive per -3 byte.

Equivalente ASCII:

.[]Y|&{yz|egp!

Rimuovi tutti i caratteri tranne [], quindi rimuovi []fino a quando la stringa non cambia più. Restituisce 1se la stringa finale è vuota.


1
Bello. È possibile utilizzare .[]|&per filtrare i caratteri, quindi riutilizzare il valore letterale per 11
ricorsivo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.