Vérifier le programme Brainfuck


17

Encore un autre problème d'analyse de Brainfuck, mais cette fois ... différent.

Vous travaillez dans Infinite Monkeys Incorporated, la société qui fabrique des programmes Brainfuck, pour résoudre divers problèmes intéressants (par accident, pas moins - après tout, la société crée des programmes aléatoires). Cependant, il semble que vos machines Turing rapides qui n'exécutent que Brainfuck ont ​​un petit problème coûteux d'erreurs de syntaxe - faites-en un, et l'ordinateur explose. C'est probablement un défaut de conception, mais personne n'avait pris la peine de comprendre pourquoi cela se produisait.

Comme les machines Turing (en particulier les machines rapides) sont chères (après tout, elles ont une RAM infinie qui coûte), il serait préférable de s'assurer que le programme n'a pas d'erreurs de syntaxe avant d'exécuter le code. Votre entreprise va exécuter beaucoup de code, donc la vérification manuelle ne fonctionnera pas. Écrivez un programme qui lit le code STDIN for Brainfuck et se termine avec un état de sortie défini sur autre chose que 0 (erreur) si le programme a une erreur de syntaxe (par exemple, ]est une erreur de syntaxe, car il n'y a pas de correspondance [). Quittez avec le statut de sortie réglé sur 0 si le programme est tout à fait correct.

Assurez-vous que votre programme remarque correctement les erreurs impliquant []. Vous ne voudriez pas qu'un autre ordinateur explose, n'est-ce pas? Oh, et assurez-vous que c'est aussi court que possible - votre patron paie pour des programmes courts (parce qu'il pense qu'ils sont rapides, ou quelque chose). Oh, et vous n'avez pas à coder dans Brainfuck (en fait, vous ne pouvez pas, car Brainfuck ne prend pas en charge les codes de sortie) - votre code sera exécuté sur un ordinateur normal.

Donc, comme vous pouvez le voir, votre travail consiste à vérifier si le programme Brainfuck est "valide" (a des []symboles associés ). Veuillez noter que les programmes Brainfuck peuvent avoir d'autres caractères que [], alors ne refusez pas le programme simplement parce qu'il a d'autres commandes. Le plus petit code gagne, mais vous vous soucierez probablement plus des votes positifs de toute façon.


1
Qu'en est-il des langues qui ne permettent pas de définir un code de sortie de processus?
Kendall Frey

1
@KendallFrey: Simple, utilisez autre chose. Ou dans GolfScript, intégrez du code Ruby. Peut-être que cela bloque certains langages de programmation ésotériques, mais bon ...
Konrad Borowski

1
Est-il correct de définir le code d'erreur sur autre chose que 1 (tant que ce n'est pas 0 bien sûr) en cas d'échec?
marinus

@marinus: Je ne sais pas pourquoi vous voudriez ça, mais je pense que ça va.
Konrad Borowski

@GlitchMr: car alors je peux utiliser à la GCD(a,b)place de 0 != a || b.
marinus

Réponses:


6

GolfScript, 18 caractères

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

Ce code s'exécute correctement avec le code de sortie 0 (et imprime des ordures sur stdout) si les crochets dans l'entrée sont équilibrés. Si ce n'est pas le cas, il échoue avec un code de sortie différent de zéro et affiche un message d'erreur sur stderr, par exemple:

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

ou

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

Étant donné que le défi n'a rien dit sur la sortie vers stdout / stderr, je pense que cela est admissible. Dans tous les cas, vous pouvez toujours les rediriger vers /dev/null.

Explication:

Le code {[]`?)},supprime tout sauf les crochets de l'entrée et ~évalue le résultat en tant que code GolfScript. La partie délicate est que les crochets non équilibrés sont parfaitement légaux dans GolfScript (et, en effet, mon code en inclut un!), Nous avons donc besoin d'une autre façon de faire planter le code.

L'astuce que j'utilise est de laisser une copie de l'entrée au bas de la pile, de collecter la pile entière dans un tableau (en utilisant le déséquilibré ]) et de décaler le premier élément. À ce stade, trois choses peuvent se produire:

  • Si l'entrée se termine par un non clos [, essayer de décaler un élément d'un tableau vide plantera l'interpréteur (ce qui, dans ce cas, est exactement ce que nous voulons!)
  • Si les crochets de l'entrée étaient équilibrés, l'élément décalé sera la chaîne d'entrée d'origine.
  • Sinon (si l'entrée avait une ouverture ] ou un non fermé [), ce sera un tableau.

Mon entrée originale de 14 caractères a ensuite comparé la valeur décalée avec une chaîne, ce qui se bloquerait s'il s'agissait d'un tableau imbriqué. Malheureusement, il s'avère que la comparaison d'un tableau plat (ou, en particulier, vide) avec une chaîne est également légale dans GolfScript, j'ai donc dû changer de tactique.

Ma soumission actuelle, à la place, utilise une méthode très brute pour différencier les tableaux des chaînes: elle les dévalue et essaie de trouver la première occurrence de [(code ASCII 91), qui sera nulle si et seulement si la valeur n'est pas évaluée. variable était un tableau. Si c'est le cas, la division du zéro avec lui-même déclenche le crash souhaité.

Ps. Deux autres solutions de 18 caractères sont:

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

et

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

Hélas, je n'ai pas encore trouvé de moyen plus court pour résoudre le "problème de tableau vide".


est-ce équilibré?: ][(c.-à-d. échoue-t-il dans votre programme?)
Justin

@Quincunx: il échoue, comme il se doit.
Ilmari Karonen du

Cependant, il s'est avéré accepter [[]; Je l'ai corrigé maintenant, au prix de 4 caractères, et il passe tous mes tests maintenant.
Ilmari Karonen

Si je vous comprends bien, vous aviez une solution de 14 caractères qui ne fait pas la distinction entre une chaîne et un tableau uniquement dans le cas où le tableau est vide. 1+transformera un tableau vide en tableau non vide, un tableau non vide en tableau non vide et une chaîne en chaîne.
Peter Taylor

@PeterTaylor: Oui, ça l'était .{[]`?)},~](n<. J'ai essayé votre 1+, mais il semble que le tableau doit contenir autre chose qu'un nombre (probablement pour que l'interpréteur se bloque lorsqu'il essaie de comparer récursivement un caractère avec un tableau / une chaîne). L'utilisation n+ne fonctionne pas non plus, car elle contraint le tableau à une chaîne; [n]+ fait le travail, mais me met encore à 18 caractères.
Ilmari Karonen

17

Brainfuck 76 octets

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

Cela sort de ses liens si les crochets ne sont pas équilibrés, ce qui fait que l'interpréteur / compilateur bf échoue à l'exécution et que certains d'entre eux ont des codes de sortie pour refléter cela.

nécessite eof = 0, des valeurs d'encapsulation et un nombre fini de cellules

Dans Ubuntu, vous pouvez utiliser l'interpréteur 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
J'aime la façon dont vous avez utilisé Brainfuck même si la question disait que c'était impossible.
Riking

Oh wow. Je suis honnêtement surpris. J'ai supposé que c'était impossible, mais ce n'est pas le cas.
Konrad Borowski

1
@xfix: Brainfuck est complet, il peut donc tout faire (compte tenu des contraintes d'E / S.)
marinus

2
@marinus Turing completness signifie uniquement qu'il peut effectuer tous les calculs. BrainfuckTuring est complet sans E / S car vous pouvez exécuter un programme qui calcule n'importe quel calcul et voir le résultat en inspectant sa mémoire après l'exécution. BF sans E / S rendrait les gens moins intéressés car il serait difficile de créer des utilitaires. par exemple, je n'aurais jamais pu faire mon interprète lisp .
Sylwester

14

Befunge 98 - 26 31 20 19 caractères

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

Je me suis débarrassé de certains conditionnels massifs. Maintenant, le programme fonctionne comme suit:

~:   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. 

qquitte le programme et affiche la valeur supérieure comme valeur d'erreur. La valeur d'erreur sera -1 1 s'il y en a trop ], 0 si elles sont équilibrées et positive négative s'il y en a trop [. Si le nombre est positif négatif, alors autant de valeurs absolues de ce nombre ]sont nécessaires pour équilibrer le programme.

Modifier: incrémentation et décrémentation commutées. [utilisé pour incrémenter le compteur et ]utilisé pour le décrémenter. En le commutant, j'enregistre 1 caractère, car pour la condition de sortie, je n'ai qu'à vérifier si le compteur est positif plutôt que négatif.


Ancienne version

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

Ce code fonctionne comme suit:

~    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.

Edit: réalisé que ces entrées acceptées comme ][, maintenant, il se termine chaque fois que le compte devient négatif, en utilisant

:0\`j


@JoKing c'est plutôt sympa, en utilisant la distance entre [et ]pour faire la comparaison avec les deux.
Justin

6

J ( 38 35)

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

Explication:

  • 1!:1[3: lire stdin
  • '[]'=/: créer une matrice où la première ligne est un masque de bits du [s en entrée, et la deuxième ligne est le] s.
  • 1 _1*: multipliez la première ligne par 1 et la deuxième ligne par -1.
  • +/: additionner les colonnes de la matrice, donnant une delta-indentation par caractère
  • +/\: créer un total cumulé de ces derniers, donnant un niveau d'indentation à chaque personnage
  • ({:+.<./): retourne le GCD de l'élément final ( {:) et du plus petit élément ( <./). Si tous les accolades correspondent, les deux devraient être 0ainsi cela reviendra 0. Si les accolades ne correspondent pas, il renverra une valeur différente de zéro.
  • exit: définissez la valeur de sortie sur celle-ci et quittez.

Belle ventilation ...
Chris Cashwell

6

rubis (64)

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

auparavant (68), c'était:

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

Une autre solution équivalente utilise

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

ne peut pas être utilisé sizeseul car cela donnerait de faux négatifs (!!!) lorsque le nombre total de parenthèses non équilibrées est multiple de 256


C'est du codegolf. Je suis sûr que vous pouvez supprimer des espaces ici. Ruby est quelque peu sensible aux espaces, mais il l'ignore dans de nombreux cas. Pour commencer, vous n'avez pas besoin d'espace blanc près de l' =opérateur.
Konrad Borowski

meilleure version (quelque peu inspirée par la solution sed + tr). Je ne suis pas sûr de pouvoir aller bien mieux que ça. Au moins, il n'a que 4 espaces blancs!
réécrit le

1
Et pourtant, je peux en retirer deux.
Konrad Borowski

2
Je pense que les espaces autour 0peuvent aller.
marinus

astuce spatiale génial, merci!
réécrit le

5

Perl, 30 caractères

Votre regex récursif de base Perl à la rescousse:

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

Pour ceux qui ne connaissent pas les arguments de ligne de commande utilisés ici: -0permet de définir le caractère de fin de ligne à des fins d'entrée de fichier; l'utilisation -0sans argument définit le caractère de fin de ligne sur EOF. -nlit automatiquement l'entrée (dans ce cas, le fichier entier) au $_préalable.

Si l'expression régulière correspond, elle renvoie une valeur vraie, qui est annulée à 0 pour le code de sortie. Sinon, la fausse valeur de retour donne un code de sortie de 1.


Je suppose que je devrai mieux jouer ma réponse pour battre cette solution de 30 caractères.
Justin

Là. Ma solution est maintenant beaucoup plus courte qu'auparavant. Belle solution. +1
Justin

4

bash (tr + sed) - 42

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

Si cela ne vous dérange pas, vous pouvez supprimer le dernier espace entre `et ]pour obtenir la longueur 41.


Sauf si je manque quelque chose, vous avez une utilisation inutile de catet $()( "[]"peut également être écrit comme []). J'ai accepté cette réponse, mais jusqu'à ce que je vois des améliorations de longueur, je ne vais pas voter contre cela, car même s'il est court, il pourrait être beaucoup plus court pour bash.
Konrad Borowski

Eh bien, en fait, je ne connais pas bien les scripts shell. Je ne peux pas faire fonctionner la plupart de ces travaux. J'ai remplacé $()par des backticks et j'ai fait le "[]"-> []vous avez suggéré.
shiona


1
J'aurais pu épée j'ai essayé et échoué, mais j'avais tort.
shiona

3

Perl (56 caractères)

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

La solution Perl évidente: une expression régulière récursive. Malheureusement, c'est une construction assez verbeuse.


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

à jgon: L'utilisation de 2 gardes semble être plus dense que si-alors-sinon. De plus, je ne pense pas que le vôtre vérifie que les crochets sont dans le bon ordre ("] [" passe)


oh wow, seulement vu ce commentaire quand il a été souligné aujourd'hui. Correction de mon code, mais bon, les gardes semblent être plus denses (+1).
jgon

3

C, 73 64 caractères

Merci aux suggestions de la boîte à pain (bien que cela nécessite probablement du petit endian pour fonctionner):

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

Comment ça fonctionne:

  • implicite int global i est initialisé à 0
  • c devient un int implicite qui obtient argc (initialisé à 1 mais on s'en fiche, tant que les bits supérieurs ne sont pas définis)
  • read(0,&c,1) lit un seul caractère dans l'octet de poids faible de c (sur les architectures little-endian) et retourne 0 à EOF; i+1 != 0sauf si la citation entre crochets équilibrés passe à -1; les multiplier ensemble fonctionne comme un booléen (sûr) ET qui prend un caractère de moins que&&
  • c==91est évalué à 1 pour '[', etc==93 évalue à 1 pour ']'. (Il pourrait y avoir une astuce de bricolage qui serait plus petite mais je ne pense à rien.)
  • return iquitte avec le code d'état 0 s'il est équilibré, différent de zéro s'il ne l'est pas. Renvoyer -1 viole techniquement POSIX mais personne ne s'en soucie vraiment.

La version précédente:

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

Utiliser getchar()au lieu de lire raccourcira le code et vous permettra d'utiliser (implicite) intau lieu de charpour votre variable. N'oubliez pas non plus que les globaux sont automatiquement initialisés à zéro.
boîte à pain le

Merci, j'ai oublié getchar (). Je ne sais pas comment l'aide globale aide, cependant - la définition d'un entier global i prend plus de caractères que l'utilisation de l'arg implicite.
moelleux

1
Les globaux peuvent également être implicites. En dehors d'une fonction c;définit un entier global.
boîte à pain le

Pendant ce temps, getchar (c) finit par ne pas le raccourcir, du moins en utilisant cette approche, car il doit être testé pour> = 0 d'une manière ou d'une autre, et un int implicite pour c passé dans read () n'est pas garanti pour travail dû à l'endianité.
moelleux

Et alors ][? Je suis sûr que ce n'est pas équilibré. N'avez-vous pas besoin de savoir s'il devient négatif au moins une fois?
vsz

2

Lua, 56

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

"^%b[]$"Qu'est-ce que c'est? Peux-tu expliquer? Ce n'est sûrement pas une expression régulière?
Cruncher

@Cruncher: Ce n'est pas le cas. C'est le modèle Lua, pas l'expression régulière (les modèles Lua sont similaires aux expressions régulières, mais ils ne le sont pas). Dans ce cas, il correspond au début de string ( ^), à un ensemble équilibré de [et ]avec tout ce qui se trouve entre ( %b[]) et à la fin de string ( $).
Konrad Borowski

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 []

Utilisez 0pour arrêter.


1

MATHEMATICA, 88 caractères

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

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

Avec la fonction s name like RegularExpression` et StringLengthje ne pourrai jamais gagner un contexte de golf en code texte avec Mathematica! :)
Murta

Je sais ce que tu veux dire :) ToCharacterCodeest beaucoup plus long que ça ordaussi ... PS: Qu'en est-il de la définition d'un code de sortie?
Ajasja

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

1

Rubis, 59 58

Recherche l'ouverture et la fermeture du support, en comptant [comme 1 et ]comme -1, et quitte si le nombre tombe en dessous de 0.

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

1
Surévalué et raccourci d'un caractère (j'ai supprimé les espaces après exit 1au cas où vous le demandez).
Konrad Borowski

1

Hassium , 104 octets

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);}

Complet (la note ne fonctionne pas dans l'interpréteur en ligne car input () est désactivé) ici


1

Code de la machine de Turing, 286 276 octets

Encore une fois, j'utilise la syntaxe de table de règles définie ici.

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

Termine en état haltpour accepter l'entrée et la halt-errrejeter.


halt-errpeut être plus court, comme halt*par exemple.
Erik the Outgolfer le

1

Pyth, 25 octets

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

Essayez-le en ligne!

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

1

Haskell ( 167 , 159)

Je l'ai fait surtout pour le plaisir, si quelqu'un a des suggestions pour le raccourcir, je serais heureux de les entendre :)

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

Éditer: correction d'un problème signalé dans les commentaires (ajout de 11 octets).

Edit 2: Création d'une fonction auxiliaire pour tester les prédicats à l'aide de gardes inspirés par user13350, supprimant 8 octets.



@ user202729 C'est un excellent point, c'était super négligent. Je suis sûr que c'est réparé maintenant.
jgon

1

Stax , 14 11 caractères

╬Äτ↔EªÉs «Ü

Exécuter et déboguer

Crédit à @recursive pour -3 octets.

Équivalent ASCII:

.[]Y|&{yz|egp!

Supprimez tous les caractères sauf [], puis supprimez []jusqu'à ce que la chaîne ne change plus. Retourne 1si la chaîne finale est vide.


1
Agréable. Vous pouvez utiliser .[]|&pour filtrer les caractères, puis réutiliser le littéral pour 11
récursif
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.