Guias de expansão (implementação de expansão (1))

10

Desta vez, sua tarefa é implementar uma variante do expand(1)utilitário POSIX , que expande guias para espaços.

Seu programa é pegar uma especificação de tabstop e, em seguida, ler a entrada no padrão e substituir os caracteres de tabulação na entrada pela quantidade apropriada de espaços para alcançar o próximo tabstop. O resultado deve ser gravado no padrão .

Especificação Tabstop

Uma especificação de tabstop consiste em um único número ou em uma lista de tabstops separada por vírgula. No caso de um único número, ele é repetido como se vários deles tivessem ocorrido em uma lista separada por vírgula (ou seja, 4atua como 4,8,12,16,20,...). Cada entrada em uma lista separada por vírgula é um número inteiro positivo opcionalmente prefixado por a +. Um +prefixo indica uma diferença relativa ao valor anterior na lista separada por vírgula. O primeiro valor da lista deve ser absoluto (ou seja, sem prefixo). As paradas de tabulação especificam a coluna do próximo caractere que não é de espaço (após a guia expandida), com a coluna mais à esquerda tomada como número 0. As guias devem sempre se expandir para pelo menos um espaço.

Entrada / saída

A especificação tabstop deve ser tomada como o primeiro parâmetro de linha de comando do programa ou lida do padrão como a primeira linha de entrada (terminada por uma nova linha), a seu critério. Após a leitura do tabstop, a entrada restante (todas as entradas, no caso anterior) até o EOF deve ser processada e expandida. A saída expandida deve ser gravada na saída padrão.

Todos os tabstops expandidos e todas as entradas assumem no máximo 80 colunas de largura. Todos os tabstops expandidos estão aumentando estritamente.


Exemplo

A especificação Tabstop 4,6,+2,+8é equivalente a 4,6,8,16, e com a entrada

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

é expandido para ( indica um espaço)

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

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

A pontuação é puro ; o código mais curto vence.

FireFly
fonte

Respostas:

2

GolfScript ( 77 75 caracteres)

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

Estou bastante satisfeito com a análise do 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
}%

Em seguida, adiciono múltiplos do último elemento até garantir o suficiente para chegar ao final das 80 colunas:

81,{t*}%+

Isso fornece o comportamento desejado quando apenas um tabstop foi especificado e, caso contrário, é relevante apenas nos casos em que a especificação não menciona. (NB, faz com que a lista de tab-stops retorne a 0 e repita o último elemento analisado, mas isso é irrelevante, porque quando se trata de usar a lista, procuro o primeiro elemento maior que a posição atual).

O resto é bem direto.

Peter Taylor
fonte
2

Ruby 161 145

Lê a especificação tabstop na primeira linha de entrada.

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

editar: adicionadas duas linhas que repetem a última tabulação de leitura para que as especificações de tabulação de um único número também funcionem corretamente

ié uma variável temporária para manter o último tabstop analisado. té a lista de tabstobs, analisada a partir da gets.scanlinha. Para uma boa medida, adicionamos 81 múltiplos do último tabstop analisado. o while getsloop continua até que não haja mais entrada. Para cada linha de entrada, substituímos as abas por espaços, uma por vez, porque a cadeia se move à medida que adicionamos os espaços e devemos recalcular o batente de tabulação correto.

daniero
fonte
Eu realmente não conheço Ruby, mas você pode escrever x+($1?i:0)como o mais curto $1?x+i:x?
Timwi
@Timwi Nope! Ruby é um pouco estranho com o operador ternário. Geralmente, você precisa colocar um espaço em algum lugar, porque os dois pontos ( :) também podem marcar o início de um símbolo , mas como um símbolo não pode começar com um dígito, tudo :0fica bem sem espaço. Ou alguma coisa. É estranho. Os parênteses também são cruciais.
Daniero
Essa digitalização de tabstop me parece problemática. Na t<<x+($1?i:0);i=xprimeira afirmação não muda x, muda ? Eu acho que você precisa revertê-lo comoi=x+($1?i:0);t<<i
Peter Taylor
11
Na verdade, você pode salvar 16 substituindo as duas primeiras linhas por i=t=[](já que ié garantido que não será necessário na primeira vez); simplificando a análise de tab-stop {t<<i=$2.to_i+($1?i:0)}e eliminando lcompletamente ( ijá mantém esse valor). Mas o legal de não se importar com a guia deixa de aumentar estritamente: isso economiza 4 caracteres, e eu posso emprestá-la para economizar 2. #
432 Peter Taylor
@PeterTaylor Obrigado pela contribuição! Não era diretamente de buggy, mas certamente um pouco inchado. Acho muito fácil se olhar cego em códigos como este.
Daniero
1

C, 228 caracteres

Aqui está uma solução C para começar. Ainda há um monte de golfe para fazer aqui (olhada em todos esses ifs e fors e putchars ...). Testado com o exemplo case de teste, bem como com a mesma entrada, mas 4e 8para a especificação da guia.

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