Expand tabs (implement expand (1))

10

Tugas Anda kali ini adalah mengimplementasikan varian expand(1)utilitas POSIX yang memperluas tab ke spasi.

Program Anda adalah untuk mengambil spesifikasi tabstop dan kemudian membaca input pada standar masuk dan mengganti karakter tab pada input dengan jumlah ruang yang sesuai untuk mencapai tabstop berikutnya. Hasilnya harus ditulis dengan standar keluar .

Spesifikasi tabstop

Sebuah spesifikasi tabstop terdiri dari baik satu nomor, atau daftar dipisahkan koma tabstops. Dalam kasus nomor tunggal, itu diulang seolah-olah kelipatannya terjadi dalam daftar yang dipisahkan koma (yaitu 4bertindak sebagai 4,8,12,16,20,...). Setiap entri dalam daftar yang dipisahkan koma adalah bilangan bulat positif yang secara opsional diawali oleh a +. Sebuah +awalan menunjukkan perbedaan relatif terhadap nilai sebelumnya dalam daftar dipisahkan koma. Nilai pertama dalam daftar harus mutlak (mis. Tidak diperbaiki). Tabstop menentukan kolom karakter non-spasi berikutnya (mengikuti tab diperluas), dengan kolom paling kiri diambil sebagai angka 0. Tab harus selalu diperluas ke setidaknya satu spasi.

Input output

Spesifikasi tabstop dapat diambil sebagai parameter baris perintah pertama untuk program, atau dibaca dari standar sebagai baris input pertama (diakhiri oleh baris baru), sesuai kebijakan Anda. Setelah tabstop telah dibaca, input yang tersisa (semua input, dalam kasus sebelumnya) sampai EOF akan diproses dan diperluas. Output yang diperluas harus ditulis dengan standar keluar.

Semua tabstop yang diperluas, dan semua input, diasumsikan memiliki lebar maksimum 80 kolom. Semua tabstop yang diperluas secara ketat meningkat.


Contoh

Spesifikasi tabstop 4,6,+2,+8setara dengan 4,6,8,16, dan dengan input keduanya

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

diperluas ke ( menunjukkan spasi)

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

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

Penilaian adalah murni ; kode menang paling pendek.

FireFly
sumber

Jawaban:

2

GolfScript ( 77 75 karakter)

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

Saya cukup senang dengan parsing 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
}%

Kemudian saya menambahkan beberapa elemen terakhir hingga saya dijamin memiliki cukup untuk mencapai akhir 80 kolom:

81,{t*}%+

Ini memberikan perilaku yang diinginkan ketika hanya satu tabstop ditentukan, dan sebaliknya hanya relevan dalam kasus-kasus yang spesifikasi tidak disebutkan. (NB itu membuat daftar tab-stops turun kembali ke 0 dan kemudian mengulangi elemen yang diuraikan terakhir, tetapi itu tidak relevan karena ketika datang untuk menggunakan daftar saya mencari elemen pertama lebih besar dari posisi saat ini).

Sisanya cukup mudah.

Peter Taylor
sumber
2

Ruby 161 145

Membaca spesifikasi tabstop pada baris input pertama.

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

sunting: Menambahkan dua baris yang membuat tabstop baca terakhir diulang sehingga spesifikasi tabstop dari satu nomor juga berfungsi dengan benar

iadalah variabel temporer untuk memegang tabstop yang diuraikan terakhir. tadalah daftar tabstobs, diurai dari gets.scanbaris. Untuk ukuran yang baik, kita tambahkan 81 kelipatan dari tabstop yang diuraikan terakhir. yang while getslingkaran terus terjadi sampai tidak ada lagi masukan. Untuk setiap baris input, kami mengganti tab untuk spasi, satu tab pada saat itu karena string bergerak saat kami menambahkan spasi dan kami harus menghitung ulang tabstop yang benar.

daniero
sumber
Saya tidak begitu mengenal Ruby, tetapi bisakah Anda menulis x+($1?i:0)sebagai yang lebih pendek $1?x+i:x?
Timwi
@Timwi Tidak! Ruby agak aneh dengan operator ternary. Biasanya Anda perlu meletakkan spasi di sana di suatu tempat, karena titik dua ( :) juga bisa menandai awal simbol , tetapi karena simbol tidak dapat dimulai dengan angka, :0tidak apa-apa tanpa spasi. Atau sesuatu. Itu aneh. Kurung juga sangat penting.
daniero
Pemindaian tabstop itu terlihat buggy bagi saya. Dalam t<<x+($1?i:0);i=xpernyataan pertama tidak berubah x, bukan? Saya pikir Anda perlu membalikkannya sebagaii=x+($1?i:0);t<<i
Peter Taylor
1
Bahkan Anda dapat menyimpan 16 dengan mengganti dua baris pertama dengan i=t=[](karena idijamin tidak diperlukan saat pertama kali sekitar); menyederhanakan tab-stop parse ke {t<<i=$2.to_i+($1?i:0)}, dan menghilangkan lseluruhnya ( isudah memegang nilai itu). Tapi satu yang bagus untuk tidak peduli tentang penghentian tab adalah peningkatan ketat: itu menyelamatkan Anda 4 karakter, dan saya bisa meminjamnya untuk menghemat 2.
Peter Taylor
@PeterTaylor Terima kasih atas masukannya! Itu tidak langsung buggy, tetapi tentu saja sedikit kembung. Saya merasa terlalu mudah untuk menatap diri sendiri pada kode seperti ini.
daniero
1

C, 228 karakter

Berikut adalah solusi C untuk memulai. Masih banyak golf yang bisa dilakukan di sini (lihat semua ifs dan fors dan putchars ...). Diuji dengan contoh testcase, serta dengan input yang sama tetapi 4dan 8untuk spesifikasi tab.

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
sumber