Latar Belakang
Untuk pengiriman kode-golf saya di C, saya memerlukan alat pemrosesan. Seperti di banyak bahasa lain, spasi putih sebagian besar tidak relevan dalam sumber C (tetapi tidak selalu!) - masih membuat kode jauh lebih mudah dipahami oleh manusia. Program C sepenuhnya golf yang tidak mengandung spasi kosong tunggal sering hampir tidak dapat dibaca.
Oleh karena itu, saya suka menulis kode saya dalam C untuk pengiriman kode-golf termasuk spasi dan kadang-kadang komentar, sehingga program membuat struktur yang komprehensif saat menulis. Langkah terakhir adalah menghapus semua komentar dan spasi kosong yang berlebihan. Ini adalah tugas yang membosankan dan tidak ada artinya yang harus dilakukan oleh magang program komputer.
Tugas
Tulis program atau fungsi yang menghilangkan komentar dan ruang kosong berlebihan dari beberapa sumber C "pra-golf" sesuai dengan aturan berikut:
- A
\
(garis miring terbalik) sebagai karakter terakhir dalam sebuah garis adalah garis lanjutan . Jika Anda menemukan ini, Anda harus memperlakukan baris berikut sebagai bagian dari baris logis yang sama (misalnya, Anda dapat menghapus\
dan mengikuti\n
(baris baru) sepenuhnya sebelum melakukan hal lain) - Komentar hanya akan menggunakan format satu baris, dimulai dengan
//
. Jadi untuk menghapusnya, Anda mengabaikan sisa baris logis di mana pun Anda menemukan di//
luar string literal (lihat di bawah). - Karakter
spasi adalah (spasi),
\t
(tab) dan\n
(baris baru, jadi di sini akhir dari baris logis). Saat Anda menemukan urutan spasi putih, periksa karakter non-spasi putih di sekitarnya. Jika
- keduanya adalah alfanumerik atau garis bawah (kisaran
[a-zA-Z0-9_]
) atau - keduanya
+
atau - keduanya
-
atau - yang sebelumnya adalah
/
dan yang berikutnya adalah*
lalu ganti urutannya dengan karakter spasi tunggal (
).
Jika tidak, hilangkan urutan sepenuhnya.
Aturan ini memiliki beberapa pengecualian :
- Arahan preprosesor harus muncul pada baris mereka sendiri di output Anda. Arahan preprosesor adalah garis yang dimulai dengan
#
. - Di dalam string literal atau karakter literal , Anda tidak harus menghapus spasi kosong. Setiap
"
(tanda kutip ganda) /'
(tanda kutip tunggal) yang tidak secara langsung didahului dengan jumlah garis miring terbalik yang aneh (\
) memulai atau mengakhiri string literal / karakter literal . Anda dijamin bahwa string dan karakter literal berakhir pada baris yang sama dengan yang mereka mulai. string literal dan literal karakter tidak dapat disarangkan, jadi'
di dalam string literal , serta"
di dalam literal karakter tidak memiliki arti khusus.
- keduanya adalah alfanumerik atau garis bawah (kisaran
Spesifikasi I / O
Input dan output harus berupa urutan karakter (string) termasuk karakter baris baru atau array / daftar string yang tidak mengandung karakter baris baru. Jika Anda memilih untuk menggunakan array / daftar, setiap elemen mewakili sebuah baris, sehingga baris baru tersirat setelah setiap elemen.
Anda dapat menganggap input tersebut adalah kode sumber program C yang valid. Ini juga berarti hanya berisi karakter, tab, dan baris ASCII yang dapat dicetak. Perilaku yang tidak terdefinisi pada input yang salah diijinkan.
Terkemuka dan trailing spasi / baris kosong yang tidak diperbolehkan .
Uji kasus
memasukkan
main() { printf("Hello, World!"); // hi }
keluaran
main(){printf("Hello, World!");}
memasukkan
#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)); }
keluaran
#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));}
memasukkan
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)); }
keluaran
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));}
memasukkan
x; #include <stdio.h> int main() { puts("hello // there"); }
keluaran
x; #include<stdio.h> int main(){puts("hello // there");}
input (contoh dunia nyata)
// 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(); } } }
keluaran
#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();}}}
Ini kode-golf , jadi jawaban tersingkat (dalam byte) yang valid akan menang.
Jawaban:
Pip ,
148135133138 byteByte dihitung dalam CP-1252 , jadi
¶
dan·
masing-masing satu byte. Perhatikan bahwa ini mengharapkan kode C sebagai argumen baris perintah tunggal, yang (pada baris perintah aktual) akan membutuhkan penggunaan urutan melarikan diri yang berlebihan. Ini jauh lebih mudah di Coba online!Penjelasan dari versi yang sedikit tidak diubah
Kode melakukan banyak operasi penggantian, dengan beberapa trik.
Kelanjutan backslash
Kita
RM
semua kemunculan string literalyaitu, garis miring terbalik diikuti oleh baris baru.
String dan karakter literal
Kami menggunakan pengganti regex dengan fungsi panggilan balik:
Regex cocok dengan kutipan tunggal atau ganda, diikuti oleh non-serakah
.*?
yang cocok dengan 0 atau lebih karakter, sesedikit mungkin. Kami memiliki pandangan negatif di belakang untuk memastikan bahwa karakter sebelumnya bukan backslash; kemudian kami mencocokkan jumlah backslash yang rata diikuti oleh pembatas pembukaan lagi.Fungsi panggilan balik mengambil string / karakter literal dan mendorongnya ke bagian belakang daftar
l
. Kemudian mengembalikan karakter yang dimulai dengan kode karakter 192 (À
) dan meningkat dengan setiap literal diganti. Dengan demikian, kode ditransformasikan seperti:Karakter pengganti ini dijamin tidak akan muncul dalam kode sumber, yang berarti kita dapat secara tak-gamblang menggantikan mereka nanti.
Komentar
Regex cocok dengan
//
semuanya hingga baris baru dan diganti denganx
(preset ke string kosong).Arahan preprosesor
Membungkus menjalankan karakter non-baris baru dimulai dengan masuk pound
¶
.Ruang yang seharusnya tidak dihilangkan
Ada banyak hal yang terjadi di sini. Bagian pertama menghasilkan daftar regex ini untuk menggantikan:
Perhatikan penggunaan lookaheads untuk mencocokkan, misalnya, hanya
e
didefine P printf
. Dengan begitu pertandingan ini tidak mengkonsumsiP
, yang berarti pertandingan selanjutnya dapat menggunakannya.Kami membuat daftar regex ini dengan memetakan fungsi ke daftar, di mana daftar berisi
dan fungsi melakukan ini untuk setiap elemen:
Setelah kami memiliki regex kami, kami mengganti kemunculannya dengan fungsi callback ini:
yang menggantikan menjalankan spasi putih di setiap pertandingan dengan
·
.Penghapusan dan pembersihan spasi putih
Tiga penggantian berturut-turut menggantikan sisa spasi (
w
) untuk string kosong (x
), berjalan¶
untuk baris baru, dan·
untuk ruang.Substitusi kembali string dan karakter literal
Kami menyusun daftar semua karakter yang kami gunakan sebagai pengganti literal dengan mengambil
192 + range(len(l))
dan mengonversi ke karakter. Kami kemudian dapat mengganti masing-masing dengan literal terkait dil
.Dan itu dia! String yang dihasilkan dicetak secara otomatis.
sumber
//
di dalam string literal jelas merupakan ide yang bagus untuk kasus uji, saya akan menambahkan satu besok.Haskell ,
327360418394 byteCobalah online!
Ini sangat menyenangkan untuk ditulis! Pertama
f
fungsi datang dan menghapus semua garis miring terbalik di akhir baris kemudianlines
memecahnya menjadi daftar string di baris baru. Lalu kami memetakan banyak fungsi ke baris dan menyatukan semuanya kembali. Fungsi-fungsi: Strip spasi dari kiri (t
) dan dari kanan (r.t.r
di manar
adalahreverse
); menghapus spasi putih dari tengah, mengabaikan string dan karakter literal serta menghapus komentar (w
); dan akhirnya menambahkan karakter baris baru ke akhir jika baris dimulai dengan #. Setelah semua baris digabungkan kembalig
mencari karakter # dan memastikan mereka didahului oleh baris baru.w
sedikit rumit jadi saya akan jelaskan lebih lanjut. Pertama saya periksa "//" karena diw
saya tahu saya tidak dalam string literal saya tahu ini adalah komentar jadi saya meninggalkan sisa baris. Selanjutnya saya periksa apakah head adalah pembatas untuk string atau karakter literal. Jika itu saya prepend itu dan meneruskan tongkatl
yang berjalan melalui karakter, melacak keadaan "melarikan diri" dengann
yang akan benar jika ada sejumlah tebasan yang berurutan. Ketikal
mendeteksi pembatas dan tidak dalam keadaan lolos ia melewati tongkat kembali kew
, memotong untuk menghilangkan spasi setelah literal karenaw
mengharapkan karakter pertama untuk tidak menjadi spasi. Kapanw
tidak menemukan pembatas yang digunakan span untuk mencari spasi putih di bagian ekor. Jika ada yang memeriksa apakah karakter di sekitarnya tidak dapat dihubungi dan memasukkan spasi jika demikian. Kemudian muncul kembali setelah spasi putih berakhir. Jika tidak ada spasi putih tidak ada ruang yang dimasukkan dan itu akan tetap bergerak.EDIT: Terima kasih banyak kepada @DLosc karena telah menunjukkan bug di program saya yang sebenarnya mengarah ke cara bagi saya untuk mempersingkat juga! Hore untuk pencocokan pola!
EDIT2: Saya idiot yang tidak selesai membaca spek! Terima kasih sekali lagi DLosc untuk menunjukkan hal itu!
EDIT3: Hanya melihat beberapa hal pengurangan tipe yang mengganggu yang berubah
e=elem
menjadiChar->[Char]->Bool
karena suatu alasan, sehingga terus berlanjute[a,q]
. Saya harus menambahkan tanda tangan jenis untuk memaksanya menjadi benar. Adakah yang tahu bagaimana saya bisa memperbaikinya? Saya belum pernah mengalami masalah ini di Haskell sebelumnya. TIOEDIT4: perbaikan cepat untuk bug @FelixPalmen menunjukkan kepada saya. Saya mungkin akan mencoba menurunkannya nanti ketika saya punya waktu.
EDIT5: -24 byte terima kasih kepada @Lynn! Terima kasih! Saya tidak tahu Anda bisa menetapkan hal-hal pada lingkup global menggunakan pencocokan pola seperti
n:c:z=...
itu sangat keren! Juga ide bagus membuat operator untukelem
berharap aku memikirkan itu.sumber
e x y=elem x y
(atau bahkane x=elem x
) menyelesaikan masalah Anda. (Saya berganti namae
menjadi operator(!)
,.)C,
497494490489 byteKarena kita sedang memproses C, mari kita lakukan menggunakan C! Fungsi
f()
mengambil input dari char pointerp
dan output ke pointerq
, dan mengasumsikan bahwa input ada di ASCII:Kami berasumsi bahwa file tersebut terbentuk dengan baik - string dan karakter literal ditutup, dan jika ada komentar pada baris terakhir, harus ada baris baru untuk menutupnya.
Penjelasan
Versi pra-golf hanya sedikit lebih terbaca, saya khawatir:
Ini mengimplementasikan mesin negara dengan rekursi ekor. Makro dan variabel pembantu adalah
O
untuk o utputR
untuk r ead masukan ker
V
untuk menentukan v karakter pengenal alid (sejak!isalnum('_')
)p
danq
- I / O pointer seperti yang dijelaskanr
- karakter terakhir menjadi r benar menyebalkans
- s aved baru-baru ini karakter non-spasit
- t ag ketika bekerja pada direktif preprocessorNegara kami adalah
a()
- kode C normalb()
- string literalc()
- komentard()
- kode C normal, setelah membacar
e()
- urutan pelarianf()
- keadaan awal (fungsi utama)g()
- di ruang putihh()
- di ruang kosong - kirim keg()
ataui()
i()
- segera setelah spasi - apakah kita perlu memasukkan karakter spasi?j()
- spasi awal - tidak pernah memasukkan karakter spasiProgram uji
Ini menghasilkan
Batasan
Ini memecah definisi seperti
dengan menghapus ruang yang memisahkan nama dari ekspansi, memberi
dengan makna yang sama sekali berbeda. Kasing ini tidak ada di set tes, jadi saya tidak akan mengatasinya.
Saya kira saya mungkin dapat menghasilkan versi yang lebih pendek dengan konversi multi-pass in-place - saya mungkin akan mencobanya minggu depan.
sumber
=
di akhir definisiO
dan mengubah ruang yang mengikuti setiap panggilanO
menjadi a=
.O'\\'
danO' '
keduanya mendapatkan spasi.C,
705663640 byteTerima kasih kepada @ Zacharý untuk bermain golf 40 byte dan terima kasih kepada @Nahuel Fouilleul untuk bermain golf 23 byte!
Cobalah online!
sumber
for(;W;C++){}
menjadifor(;W;C++);
?Perl 5,
250 + 3 (-00n), 167 + 1 (-p) byteCobalah online
sumber
Python 2 ,
479456445434502497 byteCobalah online!
Edit: Tetap untuk memasukkan
- -
,+ +
, dan/ *
sumber