Développez les onglets (implémentez expand (1))

10

Votre tâche consiste cette fois à implémenter une variante de l' expand(1)utilitaire POSIX qui étend les tabulations aux espaces.

Votre programme consiste à prendre une spécification tabstop puis à lire l'entrée sur la norme dans et à remplacer les caractères de tabulation dans l'entrée par la quantité appropriée d'espaces pour atteindre le tabstop suivant. Le résultat doit être écrit en sortie standard .

Spécification Tabstop

Une spécification tabstop se compose soit d'un numéro unique, soit d'une liste de tabstops séparés par des virgules. Dans le cas d'un nombre unique, il est répété comme si plusieurs de celui-ci se produisaient dans une liste séparée par des virgules (c'est-à-dire 4agissant comme 4,8,12,16,20,...). Chaque entrée dans une liste séparée par des virgules est un entier positif éventuellement préfixé par a +. Un +préfixe indique une différence relative par rapport à la valeur précédente dans la liste séparée par des virgules. La première valeur de la liste doit être absolue (c'est-à-dire non préfixée). Les tabstops spécifient la colonne du caractère non espace suivant (après l'onglet développé), la colonne la plus à gauche étant prise comme numéro 0. Les onglets doivent toujours s'étendre jusqu'à au moins un espace.

Entrée sortie

La spécification tabstop doit être prise comme premier paramètre de ligne de commande du programme, ou lue depuis la norme comme première ligne d'entrée (terminée par une nouvelle ligne), à ​​votre discrétion. Après la lecture du tabstop, l'entrée restante (toutes les entrées, dans le premier cas) jusqu'à EOF doit être traitée et développée. La sortie étendue doit être écrite en sortie standard.

Tous les tabstops étendus et toutes les entrées sont supposés avoir une largeur maximale de 80 colonnes. Tous les tabstops étendus augmentent strictement.


Exemple

La spécification Tabstop 4,6,+2,+8est équivalente à 4,6,8,16, et avec à la fois l'entrée

ab<Tab>c
<Tab><Tab>d<Tab>e<Tab>f

est développé en ( indique un espace)

ab␣␣c
␣␣␣␣␣␣d␣e␣␣␣␣␣␣␣f

01234567890123456   (Ruler for the above, not part of the output)
          1111111

La notation est du pur ; le code le plus court gagne.

Luciole
la source

Réponses:

2

GolfScript ( 77 75 caractères)

n/(','/{'+'/{~t++}*~:t}%81,{t*}%+:T;{[0\{.9={;T{1$>}?(.)@-' '*}*\)}/;]n+}/;

Je suis très satisfait de l'analyse syntaxique tabspec.

# Split on commas
','/
# For each element:
{
    # Split on '+'
    '+'/
    # We now have either ["val"] or ["" "val"]
    # The clever bit: fold
    # Folding a block over a one-element array gives that element, so ["val"] => "val"
    # Folding a block over a two-element array puts both elements on the stack and executes,
    # so ["" "val"]{~t++}* evaluates as
    #     "" "val" ~t++
    # which evaluates val, adds the previous value, and concatenates with that empty string
    {~t++}*
    # Either way we now have a string containing one value. Eval it and assign to t
    ~:t
}%

Ensuite, j'ajoute des multiples du dernier élément jusqu'à ce que je sois assuré d'en avoir assez pour atteindre la fin des 80 colonnes:

81,{t*}%+

Cela donne le comportement souhaité lorsqu'un seul tabstop a été spécifié, et n'est par ailleurs pertinent que dans les cas que la spécification ne mentionne pas. (NB, la liste des taquets de tabulation remonte à 0, puis répète le dernier élément analysé, mais ce n'est pas pertinent car quand il s'agit d'utiliser la liste, je recherche le premier élément supérieur à la position actuelle).

Le reste est assez simple.

Peter Taylor
la source
2

Ruby 161 145

Lit la spécification tabstop sur la première ligne d'entrée.

i=t=[]
gets.scan(/(\+)?(\d+)/){t<<i=$2.to_i+($1?i:0)}
81.times{|j|t<<j*i}
while gets
$_.sub!$&," "*(t.find{|s|s>i=$`.size}-i)while~/\t/
print
end

edit: Ajout de deux lignes qui font répéter le dernier tabstop lu afin que les spécifications tabstop d'un seul numéro fonctionnent également correctement

iest une variable temporaire pour contenir le dernier tabstop analysé. test la liste des tabulations, analysée à partir de la gets.scanligne. Pour faire bonne mesure, nous ajoutons 81 multiples du dernier tabstop analysé. la while getsboucle continue jusqu'à ce qu'il n'y ait plus d'entrée. Pour chaque ligne d'entrée, nous substituons des tabulations aux espaces, un onglet à la fois car la chaîne se déplace lorsque nous ajoutons les espaces et nous devons recalculer le tabstop correct.

daniero
la source
Je ne connais pas vraiment Ruby, mais pouvez-vous écrire x+($1?i:0)comme le plus court $1?x+i:x?
Timwi
@Timwi Nope! Ruby est un peu étrange avec l'opérateur ternaire. Habituellement, vous devez y placer un espace quelque part, car le :signe deux-points ( ) pourrait également marquer le début d'un symbole , mais comme un symbole ne peut pas commencer par un chiffre, :0c'est OK sans espace. Ou quelque chose. C'est étrange. Les parenthèses sont cruciales aussi semble-t-il.
daniero
Cette analyse tabstop me semble boguée. Dans t<<x+($1?i:0);i=xla première déclaration ne change pas x, n'est-ce pas? Je pense que vous devez l'inverser commei=x+($1?i:0);t<<i
Peter Taylor
1
En fait, vous pouvez économiser 16 en remplaçant les deux premières lignes par i=t=[](car il iest garanti de ne pas être nécessaire la première fois); simplifier l'analyse par tabulation {t<<i=$2.to_i+($1?i:0)}et éliminer lcomplètement ( icontient déjà cette valeur). Mais sympa de ne pas se soucier que la tabulation cesse d'être strictement augmentée: cela vous fait économiser 4 caractères, et je peux l'emprunter pour en économiser 2.
Peter Taylor
@PeterTaylor Merci pour la contribution! Ce n'était pas directement buggé, mais certainement un peu gonflé. Je trouve trop facile de se regarder aveuglément sur un code comme celui-ci.
daniero
1

C, 228 caractères

Voici une solution C pour commencer. Il y a encore beaucoup de golf à faire ici (regardez tous ces ifs et fors et putchars ...). Testé avec l'exemple de testcase, ainsi qu'avec la même entrée mais 4et 8pour la spécification d'onglet.

S[99],i;L,C;main(v){for(v=1;v;)v=scanf("+%d",&C),v=v>0?C+=L:scanf("%d",&C),
v&&(S[L=C]=++i,getchar());for(;i==1&&C<80;)S[C+=L]=1;for(C=L=0;C=~getchar();)
if(C+10)putchar(~C),L+=C+11?1:-L;else for(putchar(32);!S[++L];)putchar(32);}
Luciole
la source