Contexte
Pour mes soumissions de code-golf en C, j'ai besoin d'un outil de traitement. Comme dans de nombreux autres langages, les espaces blancs ne sont généralement pas pertinents dans la source C (mais pas toujours!) - rendent le code beaucoup plus compréhensible pour les humains. Un programme C entièrement joué qui ne contient pas un seul espace redondant est souvent à peine lisible.
Par conséquent, j'aime écrire mon code en C pour une soumission de code-golf comprenant des espaces et parfois des commentaires, afin que le programme conserve une structure compréhensible lors de l'écriture. La dernière étape consiste à supprimer tous les commentaires et les espaces blancs redondants. C'est une tâche fastidieuse et insensée qui devrait vraiment être effectuée par un stagiaire dans un programme informatique.
Tâche
Écrivez un programme ou une fonction qui élimine les commentaires et les espaces blancs redondants d'une source C "pré-golfée" selon les règles suivantes:
- Un
\
(barre oblique inversée) comme dernier caractère d'une ligne est une continuation de ligne . Si vous trouvez cela, vous devez traiter la ligne suivante comme faisant partie de la même ligne logique (vous pouvez par exemple supprimer complètement le\
et le suivant\n
(nouvelle ligne) avant de faire quoi que ce soit d'autre) - Les commentaires utiliseront uniquement le format d'une ligne, en commençant par
//
. Donc, pour les supprimer, vous ignorez le reste de la ligne logique partout où vous rencontrez en//
dehors d'un littéral de chaîne (voir ci-dessous). - Les caractères d'
espacement sont (espace),
\t
(tabulation) et\n
(nouvelle ligne, donc voici la fin d'une ligne logique). Lorsque vous trouvez une séquence d'espaces blancs, examinez les caractères non blancs qui l'entourent. Si
- les deux sont alphanumériques ou soulignés (plage
[a-zA-Z0-9_]
) ou - les deux sont
+
ou - les deux sont
-
ou - celui qui précède est
/
et le suivant est*
puis remplacez la séquence par un seul caractère espace (
).
Sinon, supprimez complètement la séquence.
Cette règle comporte quelques exceptions :
- Les directives du préprocesseur doivent apparaître sur leurs propres lignes dans votre sortie. Une directive de préprocesseur est une ligne commençant par
#
. - À l'intérieur d'un littéral de chaîne ou d' un littéral de caractère , vous ne devez supprimer aucun espace. Tout
"
(guillemet double) /'
(guillemet simple) qui n'est pas directement précédé d'un nombre impair de barres obliques inverses (\
) démarre ou termine un littéral de chaîne / un littéral de caractère . Vous êtes assuré que les littéraux de chaîne et de caractère se terminent sur la même ligne qu'ils ont commencée. les littéraux de chaîne et les littéraux de caractère ne peuvent pas être imbriqués, donc l''
intérieur d'un littéral de chaîne , ainsi que l'"
intérieur d'un littéral de caractère n'ont pas de signification particulière.
- les deux sont alphanumériques ou soulignés (plage
Spécifications d'E / S
L'entrée et la sortie doivent être soit des séquences de caractères (chaînes), y compris des caractères de nouvelle ligne, soit des tableaux / listes de chaînes qui ne contiennent pas de caractères de nouvelle ligne. Si vous choisissez d'utiliser des tableaux / listes, chaque élément représente une ligne, donc les sauts de ligne sont implicites après chaque élément.
Vous pouvez supposer que l'entrée est un code source de programme C valide. Cela signifie également qu'il ne contient que des caractères ASCII imprimables, des tabulations et des retours à la ligne. Un comportement non défini sur une entrée mal formée est autorisé.
Les espaces vides de début et de fin ne sont pas autorisés .
Cas de test
contribution
main() { printf("Hello, World!"); // hi }
production
main(){printf("Hello, World!");}
contribution
#define max(x, y) \ x > y ? x : y #define I(x) scanf("%d", &x) a; b; // just a needless comment, \ because we can! main() { I(a); I(b); printf("\" max \": %d\n", max(a, b)); }
production
#define max(x,y)x>y?x:y #define I(x)scanf("%d",&x) a;b;main(){I(a);I(b);printf("\" max \": %d\n",max(a,b));}
contribution
x[10];*c;i; main() { int _e; for(; scanf("%d", &x) > 0 && ++_e;); for(c = x + _e; c --> x; i = 100 / *x, printf("%d ", i - --_e)); }
production
x[10];*c;i;main(){int _e;for(;scanf("%d",&x)>0&&++_e;);for(c=x+_e;c-->x;i=100/ *x,printf("%d ",i- --_e));}
contribution
x; #include <stdio.h> int main() { puts("hello // there"); }
production
x; #include<stdio.h> int main(){puts("hello // there");}
entrée (un exemple du monde réel)
// often used functions/keywords: #define P printf( #define A case #define B break // loops for copying rows upwards/downwards are similar -> macro #define L(i, e, t, f, s) \ for (o=i; o e;){ strcpy(l[o t], l[o f]); c[o t]=c[s o]; } // range check for rows/columns is similar -> macro #define R(m,o) { return b<1|b>m ? m o : b; } // checking for numerical input is needed twice (move and print command): #define N(f) sscanf(f, "%d,%d", &i, &j) || sscanf(f, ",%d", &j) // room for 999 rows with each 999 cols (not specified, should be enough) // also declare "current line pointers" (*L for data, *C for line length), // an input buffer (a) and scratch variables r, i, j, o, z, c[999], *C, x=1, y=1; char a[999], l[999][999], (*L)[999]; // move rows down from current cursor position D() { L(r, >y, , -1, --) r++ ? strcpy(l[o], l[o-1]+--x), c[o-1]=x, l[o-1][x]=0 : 0; c[y++] = strlen(l[o]); x=1; } // move rows up, appending uppermost to current line U() { strcat(*L, l[y]); *C = strlen(*L); L(y+1, <r, -1, , ++) --r; *l[r] = c[r] = 0; } // normalize positions, treat 0 as max X(b) R(c[y-1], +1) Y(b) R(r, ) main() { for(;;) // forever { // initialize z as current line index, the current line pointers, // i and j for default values of positioning z = i = y; L = l + --z; C = c + z; j = x; // prompt: !r || y/r && x > *C ? P "end> ") : P "%d,%d> ", y, x); // read a line of input (using scanf so we don't need an include) scanf("%[^\n]%*c", a) // no command arguments -> make check easier: ? a[2] *= !!a[1], // numerical input -> have move command: // calculate new coordinates, checking for "relative" N(a) ? y = Y(i + (i<0 | *a=='+') * y) , x = X(j + (j<0 || strchr(a+1, '+')) * x) :0 // check for empty input, read single newline // and perform <return> command: : ( *a = D(), scanf("%*c") ); switch(*a) { A 'e': y = r; x = c[r-1] + 1; B; A 'b': y = 1; x = 1; B; A 'L': for(o = y-4; ++o < y+2;) o<0 ^ o<r && P "%c%s\n", o^z ? ' ' : '>', l[o]); for(o = x+1; --o;) P " "); P "^\n"); B; A 'l': puts(*L); B; A 'p': i = 1; j = 0; N(a+2); for(o = Y(i)-1; o<Y(j); ++o) puts(l[o]); B; A 'A': y = r++; strcpy(l[y], a+2); x = c[y] = strlen(a+2); ++x; ++y; B; A 'i': D(); --y; x=X(0); // Commands i and r are very similar -> fall through // from i to r after moving rows down and setting // position at end of line: A 'r': strcpy(*L+x-1, a+2); *C = strlen(*L); x = 1; ++y > r && ++r; B; A 'I': o = strlen(a+2); memmove(*L+x+o-1, *L+x-1, *C-x+1); *C += o; memcpy(*L+x-1, a+2, o); x += o; B; A 'd': **L ? **L = *C = 0, x = 1 : U(); y = y>r ? r : y; B; A 'j': y<r && U(); } } }
production
#define P printf( #define A case #define B break #define L(i,e,t,f,s)for(o=i;o e;){strcpy(l[o t],l[o f]);c[o t]=c[s o];} #define R(m,o){return b<1|b>m?m o:b;} #define N(f)sscanf(f,"%d,%d",&i,&j)||sscanf(f,",%d",&j) r,i,j,o,z,c[999],*C,x=1,y=1;char a[999],l[999][999],(*L)[999];D(){L(r,>y,,-1,--)r++?strcpy(l[o],l[o-1]+--x),c[o-1]=x,l[o-1][x]=0:0;c[y++]=strlen(l[o]);x=1;}U(){strcat(*L,l[y]);*C=strlen(*L);L(y+1,<r,-1,,++)--r;*l[r]=c[r]=0;}X(b)R(c[y-1],+1)Y(b)R(r,)main(){for(;;){z=i=y;L=l+--z;C=c+z;j=x;!r||y/r&&x>*C?P"end> "):P"%d,%d> ",y,x);scanf("%[^\n]%*c",a)?a[2]*=!!a[1],N(a)?y=Y(i+(i<0|*a=='+')*y),x=X(j+(j<0||strchr(a+1,'+'))*x):0:(*a=D(),scanf("%*c"));switch(*a){A'e':y=r;x=c[r-1]+1;B;A'b':y=1;x=1;B;A'L':for(o=y-4;++o<y+2;)o<0^o<r&&P"%c%s\n",o^z?' ':'>',l[o]);for(o=x+1;--o;)P" ");P"^\n");B;A'l':puts(*L);B;A'p':i=1;j=0;N(a+2);for(o=Y(i)-1;o<Y(j);++o)puts(l[o]);B;A'A':y=r++;strcpy(l[y],a+2);x=c[y]=strlen(a+2);++x;++y;B;A'i':D();--y;x=X(0);A'r':strcpy(*L+x-1,a+2);*C=strlen(*L);x=1;++y>r&&++r;B;A'I':o=strlen(a+2);memmove(*L+x+o-1,*L+x-1,*C-x+1);*C+=o;memcpy(*L+x-1,a+2,o);x+=o;B;A'd':**L?**L=*C=0,x=1:U();y=y>r?r:y;B;A'j':y<r&&U();}}}
C'est le code-golf , donc la réponse valide la plus courte (en octets) l'emporte.
Réponses:
Pip ,
148135133138 138 octetsLes octets sont comptés dans CP-1252 , donc
¶
et·
sont un octet chacun. Notez que cela attend le code C comme un argument de ligne de commande unique, qui (sur une ligne de commande réelle) nécessiterait l'utilisation de séquences d'échappement copieuses. C'est beaucoup plus simple sur Essayez-le en ligne!Explication de la version légèrement non golfée
Le code effectue un tas d'opérations de remplacement, avec quelques astuces.
Poursuite de la barre oblique inverse
Nous avons
RM
tous les occurrences de la chaîne littéralec'est-à-dire une barre oblique inverse suivie d'une nouvelle ligne.
Littéraux de chaîne et de caractère
Nous utilisons un remplacement d'expression régulière avec une fonction de rappel:
L'expression régulière correspond à une citation simple ou double, suivie d'une non gourmande
.*?
qui correspond à 0 ou plusieurs caractères, le moins possible. Nous avons un lookbehind négatif pour nous assurer que le personnage précédent n'était pas une barre oblique inverse; nous faisons alors correspondre un nombre pair de barres obliques inverses, suivi à nouveau du délimiteur d'ouverture.La fonction de rappel prend le littéral chaîne / caractère et le pousse à l'arrière de la liste
l
. Il retourne ensuite un caractère commençant par le code de caractère 192 (À
) et augmentant avec chaque littéral remplacé. Ainsi, le code est transformé comme suit:Ces caractères de remplacement sont garantis de ne pas apparaître dans le code source, ce qui signifie que nous pouvons les remplacer sans ambiguïté ultérieurement.
commentaires
Le regex correspond à
//
plus tout jusqu'à la nouvelle ligne et remplace parx
(prédéfini sur la chaîne vide).Directives du préprocesseur
Encapsule des séries de caractères non-newline commençant par une connexion dièse
¶
.Espaces à ne pas supprimer
Il se passe beaucoup de choses ici. La première partie génère cette liste de regex à remplacer:
Notez l'utilisation de lookaheads pour faire correspondre, par exemple, juste le
e
indefine P printf
. De cette façon, cette correspondance ne consomme pas leP
, ce qui signifie que la correspondance suivante peut l'utiliser.Nous générons cette liste d'expressions régulières en mappant une fonction à une liste, où la liste contient
et la fonction fait cela pour chaque élément:
Une fois que nous avons nos expressions régulières, nous remplaçons leurs occurrences par cette fonction de rappel:
qui remplace la série d'espaces dans chaque match avec
·
.Élimination et nettoyage des espaces blancs
Trois remplacements successifs remplacent les
w
séquences restantes de blanc ( ) par la chaîne vide (x
), les séquences de¶
pour la nouvelle ligne et l'·
espace.Substitution de substitution des littéraux de chaîne et de caractère
Nous construisons une liste de tous les caractères que nous avons utilisés comme substitutions aux littéraux en prenant
192 + range(len(l))
et en convertissant en caractères. Nous pouvons alors remplacer chacun d'eux par son littéral associé dansl
.Et c'est tout! La chaîne résultante est imprimée automatiquement.
la source
//
littéral de chaîne à l'intérieur est certainement une bonne idée pour un cas de test, j'en ajouterai un demain.Haskell ,
327360418394 octetsEssayez-le en ligne!
C'était très amusant à écrire! Tout d'abord, la
f
fonction apparaît et supprime toutes les barres obliques inverses à la fin des lignes, puis lalines
décompose en une liste de chaînes aux nouvelles lignes. Ensuite, nous mappons un tas de fonctions sur les lignes et les concaténons toutes ensemble. Ces fonctions: supprimer les espaces blancs de gauche (t
) et de droite (r.t.r
oùr
estreverse
); supprimer les espaces du milieu, en ignorant les littéraux de chaîne et de caractères ainsi que la suppression des commentaires (w
); et ajoute enfin un caractère de nouvelle ligne à la fin si la ligne commence par un #. Une fois que toutes les lignes sont concaténées, ellesg
recherchent # caractères et s'assurent qu'elles sont précédées d'une nouvelle ligne.w
est un peu complexe, je vais donc l'expliquer davantage. Je vérifie d'abord "//" carw
je sais que je ne suis pas dans une chaîne littérale, je sais que c'est un commentaire, donc je laisse tomber le reste de la ligne. Ensuite, je vérifie si la tête est un délimiteur pour une chaîne ou un littéral de caractère. Si c'est le cas, je le fais précéder et je passe le témoin versl
lequel parcourent les personnages, en suivant l'état "d'échappement" avecn
lequel il sera vrai s'il y a eu un nombre pair de barres obliques consécutives. Lorsqu'ill
détecte un délimiteur et n'est pas dans l'état d'échappement, il passe le relais àw
, le découpage pour éliminer les espaces après le littéral, car ilw
s'attend à ce que le premier caractère ne soit pas un espace. Quandw
ne trouve pas de délimiteur, il utilise span pour rechercher des espaces dans la queue. S'il y en a, il vérifie si les personnages qui l'entourent ne peuvent pas être mis en contact et insère un espace si c'est le cas. Il se reproduit ensuite une fois l'espace vide terminé. S'il n'y avait pas d'espace, aucun espace n'est inséré et il continue quand même.EDIT: Merci beaucoup à @DLosc pour avoir signalé un bogue dans mon programme qui m'a également permis de le raccourcir! Hourra pour la correspondance des motifs!
EDIT2: Je suis un idiot qui n'a pas fini de lire la spécification! Merci encore DLosc de l'avoir signalé!
EDIT3: Je viens de remarquer une chose de réduction de type ennuyeuse qui s'est
e=elem
transforméeChar->[Char]->Bool
pour une raison quelconque, se brisant ainsie[a,q]
. J'ai dû ajouter une signature de type pour la forcer à être correcte. Est-ce que quelqu'un sait comment je pourrais résoudre ce problème? Je n'ai jamais eu ce problème à Haskell auparavant. TIOEDIT4: correction rapide du bug @FelixPalmen m'a montré. Je pourrais essayer de jouer au golf plus tard quand j'aurai du temps.
EDIT5: -24 octets grâce à @Lynn! Je vous remercie! Je ne savais pas que vous pouviez attribuer des choses sur la portée globale en utilisant la correspondance de motifs comme
n:c:z=...
si c'était vraiment cool! Aussi une bonne idée de faire un opérateur pour leelem
souhait que j'y avais pensé.la source
e x y=elem x y
(ou mêmee x=elem x
) résout votre problème. (J'ai renommée
un opérateur,.(!)
)C,
497494490489 octetsPuisque nous traitons C, faisons-le en utilisant C! La fonction
f()
prend l'entrée du pointeur charp
et les sorties vers le pointeurq
, et suppose que l'entrée est en ASCII:Nous supposons que le fichier est bien formé - les littéraux de chaîne et de caractère sont fermés, et s'il y a un commentaire sur la dernière ligne, il doit y avoir une nouvelle ligne pour la fermer.
Explication
La version pré-golfée n'est que légèrement plus lisible, je le crains:
Il implémente une machine à états par récursivité de queue. Les macros et variables d'aide sont
O
pour o utputR
à r l'entrée de enr
V
pour déterminer v caractères d'identificateur alides (depuis!isalnum('_')
)p
etq
- pointeurs d'E / S comme décritr
- dernier caractère à r eads
- s Aved récent caractère non-blanct
- t ag lorsque vous travaillez sur une directive de préprocesseurNos états sont
a()
- code C normalb()
- chaîne littéralec()
- commenterd()
- code C normal, après lecturer
e()
- séquence d'échappementf()
- état initial (fonction principale)g()
- en blanch()
- en blanc - envoi versg()
oui()
i()
- immédiatement après un espace - devons-nous insérer un caractère espace?j()
- espace initial - ne jamais insérer de caractère espaceProgramme de test
Cela produit
Limitation
Cela rompt les définitions telles que
en supprimant l'espace qui sépare le nom de l'expansion, donnant
avec un sens entièrement différent. Ce cas est absent des ensembles de tests, donc je ne vais pas y remédier.
Je soupçonne que je pourrais être en mesure de produire une version plus courte avec une conversion sur place multi-passes - je pourrais essayer la semaine prochaine.
la source
=
à la fin de la définition deO
et changer l'espace qui suit chaque appel enO
en=
.O'\\'
et que lesO' '
deux ont acquis un espace.C,
705663640 octetsMerci à @ Zacharý pour avoir joué au golf 40 octets et merci à @Nahuel Fouilleul pour avoir joué au golf 23 octets!
Essayez-le en ligne!
la source
for(;W;C++){}
devenirfor(;W;C++);
?Perl 5,
250 + 3 (-00n), 167 + 1 (-p) octetsEssayez-le en ligne
la source
Python 2 ,
479456445434502497 octetsEssayez-le en ligne!
Edit: Correction d'inclure
- -
,+ +
et/ *
la source