Perl, 1116 1124 byte, n = 3, skor = 1124 ^ (2/3) atau sekitar 108.1
Memperbarui : Saya sekarang telah memverifikasi bahwa ini bekerja dengan n = 3 via brute force (yang membutuhkan beberapa hari); dengan program kompleks ini, sulit untuk memeriksa ketahanan radiasi dengan tangan (dan saya membuat satu kesalahan dalam versi sebelumnya, itulah sebabnya jumlah byte meningkat). Akhiri pembaruan
Saya sarankan mengarahkan stderr di suatu tempat yang Anda tidak akan melihatnya; program ini menghasilkan satu ton peringatan tentang sintaksis yang meragukan bahkan ketika Anda tidak menghapus karakter darinya.
Mungkin saja program ini dapat dipersingkat. Mengerjakan ini cukup menyakitkan, membuatnya mudah kehilangan kemungkinan optimasi mikro. Saya sebagian besar bertujuan untuk mendapatkan jumlah karakter yang dapat dihapus setinggi mungkin (karena itu benar-benar menantang dari program ini), dan memperlakukan tiebreak kode-golf sebagai sesuatu yang menyenangkan untuk dibidik tetapi sebagai sesuatu yang saya tidak ingin taruh. upaya konyol untuk mengoptimalkan (atas dasar itu sangat mudah untuk mematahkan resistensi radiasi secara tidak sengaja).
Program
Catatan: ada _
karakter Kontrol literal (ASCII 31) segera sebelum masing-masing dari empat kejadian -+
. Saya tidak berpikir itu menyalin dan menempel ke StackOverflow dengan benar, jadi Anda harus menambahkannya kembali sebelum menjalankan program.
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
Penjelasan
Program ini, cukup jelas, terbuat dari empat program kecil yang identik digabung bersama. Ide dasarnya adalah bahwa setiap salinan program akan memverifikasi apakah sudah rusak terlalu buruk untuk dijalankan atau tidak; jika sudah, ia tidak akan melakukan apa-apa (selain kemungkinan memuntahkan peringatan) dan membiarkan salinan selanjutnya dijalankan; jika belum (yaitu tidak ada penghapusan, atau karakter yang dihapus adalah sesuatu yang tidak membuat perbedaan pada operasi program), itu akan melakukan hal yang quiny (mencetak kode sumber program lengkap; ini adalah quine yang tepat, dengan setiap bagian yang berisi penyandian seluruh kode sumber) dan kemudian keluar (mencegah salinan tidak rusak lainnya dari mencetak kode sumber lagi dan dengan demikian merusak quine dengan mencetak terlalu banyak teks).
Setiap bagian pada gilirannya terbuat dari dua bagian yang secara fungsional independen secara efektif; bungkus luar dan beberapa kode internal. Dengan demikian, kita dapat mempertimbangkannya secara terpisah.
Pembungkus luar
Pembungkus luar, pada dasarnya, eval<+eval<+eval< ... >####>####...>###
(ditambah banyak titik koma dan baris baru yang tujuannya harus cukup jelas; itu untuk memastikan bahwa bagian-bagian dari program akan tetap terpisah terlepas dari apakah beberapa titik koma, atau baris baru sebelum mereka, dihapus ). Ini mungkin terlihat cukup sederhana, tetapi halus dalam beberapa cara, dan alasan saya memilih Perl untuk tantangan ini.
Pertama, mari kita lihat bagaimana fungsi wrapper dalam salinan program yang tidak rusak. eval
mem-parsing sebagai fungsi bawaan, yang mengambil satu argumen. Karena sebuah argumen diharapkan, +
ini adalah unary +
(yang akan sangat akrab bagi pegolf Perl sekarang; mereka sering berguna secara mengejutkan). Kami masih mengharapkan argumen (kami baru saja melihat operator yang tidak waspada), sehingga <
yang datang selanjutnya ditafsirkan sebagai awal dari <>
operator (yang tidak menggunakan argumen awalan atau postfix, dan dengan demikian dapat digunakan dalam posisi operan).
<>
adalah operator yang cukup aneh. Tujuan biasanya adalah untuk membaca filehandles, dan Anda menempatkan nama filehandle di dalam kurung sudut. Atau, jika ekspresi tidak valid sebagai nama filehandle, itu globbing (pada dasarnya, proses yang sama yang digunakan shell UNIX untuk menerjemahkan teks yang dimasukkan oleh pengguna ke urutan argumen baris perintah; versi Perl yang lebih lama sebenarnya digunakan) shell untuk ini, tetapi sekarang Perl menangani globbing secara internal). Penggunaan yang dimaksudkan, oleh karena itu, di sepanjang garis <*.c>
, yang biasanya akan mengembalikan daftar seperti ("foo.c", "bar.c")
. Dalam konteks skalar (seperti argumen untukeval
), ia hanya mengembalikan entri pertama yang ditemukan pertama kali dijalankan (setara dengan argumen pertama), dan akan mengembalikan entri lain pada hipotetis masa depan yang tidak pernah terjadi.
Sekarang, kerang sering menangani argumen baris perintah; jika Anda memberikan sesuatu seperti -r
tanpa argumen, itu hanya akan diteruskan ke program kata demi kata, terlepas dari apakah ada file dengan nama itu atau tidak. Perl bertindak dengan cara yang sama, jadi selama kami memastikan bahwa tidak ada karakter yang khusus untuk shell atau untuk Perl antara <
dan yang cocok >
, kami dapat secara efektif menggunakan ini seperti bentuk string literal yang benar-benar canggung. Bahkan lebih baik, parser Perl untuk operator seperti kutipan memiliki kecenderungan kompulsif untuk mencocokkan tanda kurung bahkan dalam konteks seperti ini di mana tidak masuk akal, jadi kita bisa membuat sarang <>
dengan aman (yang merupakan penemuan yang diperlukan untuk program ini dimungkinkan). Kelemahan utama dari semua ini <>
adalah bahwa melarikan diri dari isi<>
hampir mustahil; tampaknya ada dua lapisan unescaping dengan masing-masing <>
, jadi untuk menghindari sesuatu di dalam ketiganya, perlu didahului dengan 63 backslash. Saya memutuskan bahwa meskipun ukuran kode hanya pertimbangan sekunder dalam masalah ini, hampir pasti tidak ada gunanya membayar hukuman seperti ini untuk skor saya, jadi saya hanya memutuskan untuk menulis sisa program tanpa menggunakan karakter yang menyinggung.
Jadi apa yang terjadi jika bagian pembungkus terhapus?
- Penghapusan kata
eval
menyebabkannya berubah menjadi kata kunci , string tanpa arti. Perl tidak suka ini, tetapi memperlakukan mereka seolah-olah mereka dikelilingi dengan tanda kutip; dengan demikian eal<+eval<+...
ditafsirkan sebagai"eal" < +eval<+...
. Ini tidak berpengaruh pada operasi program, karena pada dasarnya hanya mengambil hasil dari evals yang sangat bersarang (yang tidak kita gunakan lagi), melemparkannya ke integer, dan melakukan beberapa perbandingan yang tidak berguna di atasnya. (Hal semacam ini menyebabkan banyak spam peringatan karena ini jelas bukan hal yang berguna untuk dilakukan dalam keadaan normal; kami hanya menggunakannya untuk menyerap penghapusan.) Ini mengubah jumlah kurung sudut penutup yang kami butuhkan (karena braket pembuka sekarang ditafsirkan sebagai operator perbandingan sebagai gantinya), tetapi rantai komentar pada akhirnya memastikan string akan berakhir dengan aman tidak peduli berapa kali bersarang. (Ada lebih banyak #
tanda daripada yang dibutuhkan di sini; saya menulisnya seperti yang saya lakukan untuk membuat program lebih kompresibel, membiarkan saya menggunakan lebih sedikit data untuk menyimpan quine.)
- Jika a
<
dihapus, kode sekarang diuraikan sebagai eval(eval<...>)
. Yang sekunder, di luar eval
tidak memiliki efek, karena program yang kami evaluasi tidak mengembalikan apa pun yang memiliki efek nyata sebagai program (jika mereka kembali secara normal, itu biasanya berupa string nol atau kata kunci; lebih umum mereka kembali melalui pengecualian, yang menyebabkan eval
untuk mengembalikan string nol, atau gunakan exit
untuk menghindari kembali sama sekali).
- Jika
+
terhapus, ini tidak memiliki efek langsung jika kode yang berdekatan itu utuh; unary +
tidak berpengaruh pada program. (Alasan yang asli +
ada untuk membantu memperbaiki kerusakan; mereka meningkatkan jumlah situasi di mana <
ditafsirkan sebagai unary <>
daripada sebagai operator relasional, artinya Anda perlu lebih banyak penghapusan untuk menghasilkan program yang tidak valid.)
Pembungkus dapat rusak dengan penghapusan yang cukup, tetapi Anda perlu melakukan serangkaian penghapusan untuk menghasilkan sesuatu yang tidak diuraikan. Dengan empat penghapusan, Anda dapat melakukan ini:
eal<evl<eval+<...
dan di Perl, operator relasional <
adalah nonassociative, dan dengan demikian Anda mendapatkan kesalahan sintaksis (yang sama dengan yang Anda dapatkan 1<2<3
). Dengan demikian, batas atas program yang ditulis adalah n = 3. Menambahkan lebih banyak unary +
tampaknya merupakan cara yang menjanjikan untuk meningkatkannya, tetapi karena hal itu akan membuat semakin mungkin bahwa bagian dalam bungkusnya juga dapat rusak, memverifikasi bahwa versi baru dari program ini bekerja bisa sangat sulit.
Alasan pembungkus sangat berharga adalah bahwa eval
di Perl menangkap pengecualian, seperti (misalnya) pengecualian yang Anda dapatkan ketika Anda mencoba untuk mengkompilasi kesalahan sintaks. Karena ini eval
adalah string literal, kompilasi string terjadi saat runtime, dan jika literal gagal dikompilasi, pengecualian yang dihasilkan tertangkap. Hal ini menyebabkan eval
untuk mengembalikan string nol dan mengatur indikator kesalahan $@
, tetapi kami tidak pernah memeriksa keduanya (kecuali dengan sesekali mengeksekusi string null yang dikembalikan dalam beberapa versi program yang dimutasi). Yang terpenting, ini berarti bahwa jika terjadi sesuatu pada kode di dalamnyawrapper, menyebabkan kesalahan sintaks, maka wrapper hanya akan menyebabkan kode tidak melakukan apa-apa sebagai gantinya (dan program akan terus mengeksekusi dalam upaya untuk menemukan salinan dirinya sendiri yang tidak rusak). Oleh karena itu, kode di dalam tidak harus hampir seperti radiasi sebagai pembungkus; semua yang kami pedulikan adalah bahwa jika rusak, ia akan bertindak secara identik dengan versi program yang tidak rusak, atau akan macet (memungkinkan eval
untuk menangkap pengecualian dan melanjutkan) atau keluar secara normal tanpa mencetak apa pun.
Di dalam bungkusnya
Kode di dalam pembungkus, pada dasarnya, terlihat seperti ini (sekali lagi, ada Kontrol - _
bahwa Stack Exchange tidak akan ditampilkan segera sebelum -+
):
eval+(q(...)=~y=A-Z=-+;-AZz-~=r)
Kode ini ditulis seluruhnya dengan karakter glob-safe, dan tujuannya adalah untuk menambahkan alfabet tanda baca baru yang memungkinkan untuk menulis program nyata, melalui transliterasi dan evaluasi string literal (kami tidak dapat menggunakan '
atau "
sebagai kutipan kami tanda, tapi q(
... )
juga merupakan cara yang valid untuk membentuk string dalam Perl). (Alasan untuk karakter yang tidak diinginkan adalah bahwa kita perlu mentransliterasi sesuatu ke dalam karakter ruang tanpa karakter spasi literal dalam program; sehingga kita membentuk rentang mulai dari ASCII 31, dan menangkap ruang sebagai elemen kedua dari jangkauan.) jelas, jika kita memproduksi beberapa karakter melalui transliterasi, kita harus mengorbankan karakter untuk transliterasi mereka dari, tetapi huruf besar tidak terlalu berguna dan lebih mudah untuk menulis tanpa akses ke mereka daripada tanpa akses ke tanda baca.
Berikut adalah alfabet tanda baca yang tersedia sebagai hasil dari glob (baris atas menunjukkan pengodean, semakin rendah karakter yang dikodekan):
BCDEFGHIJKLMNOPQRSTUVWXYZ
! "# $% & '() * +; <=>? @ AZz {|} ~
Terutama, kami memiliki banyak tanda baca yang tidak aman-glob tetapi berguna dalam menulis program Perl, bersama dengan karakter luar angkasa. Saya juga menyimpan dua huruf besar, literal A
dan Z
(yang mengkodekan bukan untuk diri mereka sendiri, tetapi untuk T
dan U
, karena A
diperlukan sebagai titik akhir atas dan bawah kisaran); ini memungkinkan kita untuk menulis instruksi transliterasi itu sendiri menggunakan set karakter yang disandikan baru (meskipun huruf besar tidak berguna, mereka berguna dalam menentukan perubahan pada huruf besar). Karakter paling terkenal yang tidak kami miliki adalah [
,, \
dan ]
, tetapi tidak diperlukan (ketika saya membutuhkan baris baru dalam output, saya memproduksinya menggunakan baris baru implisit darisay
daripada perlu menulis \n
; chr 10
akan bekerja tetapi lebih verbose).
Seperti biasa, kita perlu khawatir tentang apa yang terjadi jika bagian dalam bungkusnya rusak di luar string literal. Seorang yang rusak eval
akan mencegah apa pun berjalan; kami baik-baik saja dengan itu. Jika tanda kutip rusak, bagian dalam string tidak Perl valid, dan dengan demikian pembungkus akan menangkapnya (dan banyak pengurangan pada string berarti bahwa bahkan jika Anda bisa membuatnya Perl valid, itu tidak akan melakukan apa pun, yang adalah hasil yang dapat diterima). Kerusakan transliterasi, jika itu bukan kesalahan sintaks, akan mangle string sedang dievaluasi, biasanya menyebabkan hal itu menjadi kesalahan sintaks; Saya tidak 100% yakin tidak ada kasus di mana ini rusak, tapi saya brute-memaksa saat ini untuk memastikan, dan itu harus cukup mudah untuk memperbaikinya jika ada.
Program yang disandikan
Melihat ke dalam string literal, membalikkan pengkodean yang saya gunakan, dan menambahkan spasi putih untuk membuatnya lebih mudah dibaca, kita mendapatkan ini (sekali lagi, bayangkan garis bawah kontrol sebelum -+
, yang dikodekan sebagai A
):
$o=q<
length$o ==181 || zzzz((()));
do {
chop ($t = "eval+<"x4);
$r = '=-+;-AZz-~=';
$s = '$o=q<' . $o . '>;eval$o';
eval '$s=~y' . $r . 'A-Z=';
say "$t(q($s)=~y=A-Z${r}r)" . "####>"x6;
say ";" for 1..4
} for 1..4;
exit>;
eval $o
Orang yang terbiasa dengan quines akan mengenali struktur umum ini. Bagian yang paling penting adalah di awal, di mana kami memverifikasi bahwa $ o tidak rusak; jika karakter telah dihapus, panjangnya tidak akan cocok 181
, jadi kami berjalan zzzz((()))
yang, jika tidak kesalahan sintaks karena kurung tak tertandingi, akan menjadi kesalahan runtime bahkan jika Anda menghapus tiga karakter, karena tidak ada zzzz
, zzz
, zz
, dan z
merupakan fungsi, dan tidak ada cara untuk mencegahnya parsing sebagai fungsi selain menghapus (((
dan menyebabkan kesalahan sintaks yang tidak jelas. Cek itu sendiri juga kebal terhadap kerusakan; yang ||
bisa rusak ke |
tapi itu akan menyebabkan zzzz((()))
panggilan untuk menjalankan tanpa syarat; variabel atau konstanta yang rusak akan menyebabkan ketidakcocokan karena Anda membandingkan salah satu dari 0
,180
, 179
, 178
Kesetaraan untuk beberapa subset dari digit 181
; dan menghapus satu =
akan menyebabkan kegagalan parse, dan dua =
tidak dapat dihindari menyebabkan LHS untuk mengevaluasi baik integer 0 atau string nol, yang keduanya falsey.
Pembaruan : Pemeriksaan ini sedikit salah dalam versi program sebelumnya, jadi saya harus mengeditnya untuk memperbaiki masalah. Versi sebelumnya tampak seperti ini setelah decode:
length$o==179||zzzz((()))
dan dimungkinkan untuk menghapus tiga tanda baca pertama untuk mendapatkan ini:
lengtho179||zzz((()))
lengtho179
, menjadi juru kunci, adalah benar dan dengan demikian merusak cek. Saya memperbaikinya dengan menambahkan dua B
karakter tambahan (yang menyandikan karakter spasi), artinya versi terbaru dari quine melakukan ini:
length$o ==181||zzzz((()))
Sekarang tidak mungkin menyembunyikan =
tanda dan $
tanda tanpa menghasilkan kesalahan sintaksis. (Saya harus menambahkan dua spasi daripada satu karena panjang 180
akan menempatkan 0
karakter literal ke dalam sumber, yang dapat disalahgunakan dalam konteks ini untuk integer-bandingkan nol dengan bareword, yang berhasil.) Akhiri pembaruan
Setelah pemeriksaan panjang berlalu, kita tahu salinannya tidak rusak, setidaknya dalam hal penghapusan karakter dari itu, jadi itu semua hanya langsung quining dari sana (penggantian tanda baca karena tabel decoding yang rusak tidak akan tertangkap dengan pemeriksaan ini , tapi saya sudah memverifikasi melalui brute-forcing bahwa tidak ada tiga penghapusan dari hanya tabel decoding yang memecahkan quine; mungkin sebagian besar dari mereka menyebabkan kesalahan sintaksis). Kita sudah memiliki $o
variabel, jadi yang perlu kita lakukan hanyalah meng-hardcode pembungkus luar (dengan sedikit tingkat kompresi; saya tidak melewatkan bagian kode-golf dari pertanyaan sepenuhnya ). Salah satu triknya adalah kita menyimpan sebagian besar tabel penyandian di$r
; kita dapat mencetaknya secara harfiah untuk menghasilkan bagian tabel penyandian dari pembungkus dalam, atau menggabungkan beberapa kode di sekitarnya dan eval
itu untuk menjalankan proses decoding secara terbalik (memungkinkan kita untuk mencari tahu apa versi $ o yang dikodekan adalah , hanya memiliki versi yang diterjemahkan yang tersedia pada saat ini).
Akhirnya, jika kami adalah salinan utuh dan dengan demikian dapat menampilkan seluruh program asli, kami memanggil exit
untuk mencegah salinan lain juga mencoba untuk mencetak program.
Skrip verifikasi
Tidak terlalu cantik, tetapi mempostingnya karena seseorang bertanya. Saya menjalankan ini beberapa kali dengan berbagai pengaturan (biasanya berubah $min
dan $max
untuk memeriksa berbagai bidang yang menarik); itu bukan proses yang sepenuhnya otomatis. Ini memiliki kecenderungan untuk berhenti berjalan karena beban CPU yang berat di tempat lain; ketika ini terjadi, saya baru saja mengubah $min
ke nilai pertama $x
yang tidak sepenuhnya diperiksa dan terus menjalankan skrip (sehingga memastikan bahwa semua program dalam jangkauan akhirnya diperiksa). Saya hanya memeriksa penghapusan dari salinan pertama program, karena cukup jelas bahwa penghapusan dari salinan lain tidak dapat berbuat lebih banyak.
use 5.010;
use IPC::Run qw/run/;
undef $/;
my $program = <>;
my $min = 1;
my $max = (length $program) / 4 - 3;
for my $x ($min .. $max) {
for my $y ($x .. $max) {
for my $z ($y .. $max) {
print "$x, $y, $z\n";
my $p = $program;
substr $p, $x, 1, "";
substr $p, $y, 1, "";
substr $p, $z, 1, "";
alarm 4;
run [$^X, '-M5.010'], '<', \$p, '>', \my $out, '2>', \my $err;
if ($out ne $program) {
print "Failed deleting at $x, $y, $z\n";
print "Output: {{{\n$out}}}\n";
exit;
}
}
}
}
say "All OK!";
Subleq
. Saya pikir ini akan ideal untuk tantangan semacam ini!