Sebuah Quine adalah program yang menghasilkan output yang identik dengan kode sumber program. Di situs web ini, kami umumnya hanya peduli pada quine yang tepat (pada saat penulisan, definisi saat ini adalah "beberapa bagian dari output dikodekan oleh bagian yang berbeda dari program").
Apa saran yang Anda miliki untuk menulis quine yang tepat, atau program dengan properti seperti quine? Seperti biasa, setiap tip harus dalam jawaban yang berbeda.
Gunakan evaluntuk mengurangi kebutuhan untuk menyalin kode
Mayoritas quine membutuhkan dua salinan kode; satu untuk dieksekusi, satu sebagai data. Ini bisa berakhir dengan menggandakan panjang kode sumber, membuatnya lebih sulit untuk dipertahankan, dan memperburuk skor jika Anda sedang menulis quine untuk kompetisi kode-golf .
Menggabungkan dua salinan berarti bahwa satu informasi perlu digunakan untuk dua tujuan. Mencoba memperlakukan kode Anda karena data sering kali tidak mungkin, dan biasanya dianggap curang. Memperlakukan data sebagai kode, bagaimanapun, dapat dilakukan dalam banyak bahasa melalui penggunaan builtin, biasanya disebut eval. Dengan demikian, quine Anda pada dasarnya terdiri dari penyimpanan badan utama quine Anda dalam suatu variabel (sehingga Anda dapat merujuknya lebih dari satu kali), kemudian mengevaluasi variabel itu.
Berikut adalah contoh cara kerjanya (contohnya ditulis dengan Python, tetapi sesuatu yang serupa bekerja dalam banyak bahasa lain):
@QPaysTaxes: Saya bertujuan untuk membuat kode saya di sini sebagai dapat dibaca dan dikelola sebagaimana kondisi kemenangan memungkinkan. Sayangnya, itu masih biasanya dibedakan dari garis ASCII kebisingan (atau garis kebisingan hanya biasa jika saya menggunakan Jelly) dari orang yang tidak digunakan untuk bahasa.
14
Manfaatkan pemformatan string
Salah satu cara termudah untuk membuat quine adalah dengan mendefinisikan string, kemudian memasukkan string ke dalam dirinya dengan pemformatan string.
s='s=%r;print s%%s';print s%s
Jadi dalam contoh ini Python quine, kita mendeklarasikan string dengan bagian pertama sama dengan apa pun yang ada sebelum string s=, lalu kita membiarkan string dimasukkan dengan format %r, dan akhirnya kita meletakkan apa yang muncul setelah string untuk mencetak dan memformatnya . Trailing newline adalah karena printmencetak trailing newline.
Jadi templatnya benar-benar seperti ini, dengan Python:
<A>'<A>%r<B>'<B>
Untuk memperluas kuin yang ada dengan lebih banyak kode:
Dalam beberapa bahasa, objek fungsi (atau konstruksi yang setara) secara implisit menyimpan kode sumbernya, dan akan mengembalikannya ketika dikonversi menjadi string. Ini memungkinkan compact quines tanpa menggunakan string eval . Contoh penting dari bahasa tersebut adalah JavaScript:
Kode ini mendefinisikan dan memanggil fungsi fyang, ketika dipanggil, mencetak kode sumbernya sendiri diikuti oleh panggilan untuk dirinya sendiri. Satu-satunya bagian dari program yang perlu diduplikasi adalah pemanggilan fungsi f(). Tentu saja, badan fungsi dapat menyertakan "payload" kode yang sewenang-wenang yang juga akan dieksekusi ketika fungsi dipanggil.
Versi yang lebih ringkas dari trik yang sama berfungsi dalam bahasa golf GolfScript :
Masing-masing quine ini pertama-tama mendefinisikan suatu blok kode anonim (terlampir dalam kurung kurawal), yang berperilaku seperti objek fungsi dalam JavaScript: ia dapat dieksekusi, dan jika diubah, ia mengembalikan kode sumbernya. Sisa kode ( .~dalam GolfScript, atau _~di CJam) kemudian mengeksekusi blok, sambil meninggalkan salinannya di tumpukan. Kode di dalam blok kemudian mendorong string ke tumpukan yang mengulang kode di luar blok. Ketika penerjemah keluar, ia secara otomatis mengencangkan dan mencetak semua yang tersisa di tumpukan. Seperti contoh JavaScript, blok kode ini juga dapat dibuat untuk membawa dan mengeksekusi payload sewenang-wenang dari kode tambahan tanpa harus menduplikatnya.
Gunakan pembatas string yang bersarang tanpa melarikan diri
Seringkali, salah satu bagian tersulit tentang menulis quine adalah langkah melarikan diri. Ini diperlukan di hampir setiap quine; masalahnya adalah Anda menyimpan data entah bagaimana, dan Anda perlu mereplikasi kode yang menyimpan data dalam output quine. Kode itu akan berisi formulir data yang lolos, sehingga program akan melihat formulir yang tidak dihapus, dan Anda harus melarikan diri kembali.
Cara termudah untuk menangani langkah unescaping adalah jika bentuk data yang lolos dan tidak lolos hanya berbeda dalam ada atau tidak adanya pembatas string. Dengan demikian, meloloskan diri merupakan masalah sederhana dengan menambahkan sepasang pembatas string baru di sekitar string. Sayangnya, ini jelas hanya bisa berfungsi jika pembatas string itu sendiri dapat diekspresikan dalam data tanpa melarikan diri.
Perl adalah contoh bahasa di mana trik ini bekerja. Meskipun pembatas string yang biasa adalah "…"atau '…', q(…)sarang yang jarang digunakan , memungkinkan jenis quine ini ditulis:
$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print
Ini adalah kode + data quine. s///adalah operasi penggantian string regex; kami menggunakan 0sebagai marker, dan mencocokkannya di dalam regex sebagai \d("digit apa saja"), untuk menghindari penggunaan marker lebih dari sekali (walaupun sebagai optimasi lain, kami sebenarnya bisa saja menggunakan 0lagi, karena Perl s///hanya menggantikan yang pertama kali muncul dengan default). Perhatikan bahwa tidak ada langkah pelarian yang eksplisit diperlukan di sini, karena q(…)pembatas hanya dapat dimasukkan secara harfiah dalam string data.
Struktur paling umum untuk quine terlihat seperti kodesemu ini:
data = " versi lolos dari seluruh program,
dengan string ini diganti dengan spidol "
program = data.replace (
ekspresi yang mengevaluasi ke penanda tetapi tidak menyebutkannya ,
lolos (data))
program cetak;
Struktur ini dapat digunakan untuk menulis quine (cukup naif) di sebagian besar bahasa. Namun, skornya cenderung buruk pada sebagian besar sistem penilaian, karena Anda harus menulis keseluruhan program dua kali. Namun, sebagian besar struktur quine dapat dianggap optimalisasi yang satu ini.
Ada beberapa kehalusan untuk ini. Dalam beberapa bahasa, bagian tersulit dalam melakukan operasi ini adalah menulis kode pelolosan; dalam banyak bahasa, membuat penanda tanpa menyebutkan namanya itu sulit; dan dalam beberapa bahasa esoterik, Anda harus menemukan jenis string literal Anda sendiri. Ketiga operasi cenderung tidak menyebabkan terlalu banyak masalah.
Sebagai contoh, kita dapat menulis quine Python yang keluar dari string menggunakan repr, dan menggunakan string 2-karakter-urutan x"(yang direpresentasikan sebagai "x\"", yaitu tidak menggunakan urutan x"dalam representasi string dari string itu sendiri) sebagai penanda:
Mungkin perlu dicatat (mungkin dalam jawaban lain) bahwa memasukkan string ke posisi marker sering kali mahal di esolang, dan mungkin perlu penataan kode sehingga string itu sendiri adalah hal pertama atau terakhir (mungkin terpisah dari akhir dengan satu atau dua karakter yang dapat Anda hardcode) sehingga Anda tahu ke mana harus pergi.
Martin Ender
@ MartinEnder: Saya setuju bahwa itu layak disebut, tapi itu mungkin jawaban lain (daripada komentar atau edit dalam jawaban ini). Sebagian besar tip quine adalah modifikasi pada struktur umum ini, jadi saya ingin membagikannya terlebih dahulu, karena banyak orang tidak tahu harus mulai dari mana untuk menulis quine.
Alternatif untuk marker adalah menggunakan dua string, saya melakukan itu untuk Glass .
Ørjan Johansen
4
Mengeksploitasi kode sumber pembungkus
Dalam beberapa bahasa (kebanyakan bahasa 2D), kode sumber dapat digunakan; dalam keadaan tertentu (misalnya di Befunge-98, jika program Anda adalah satu-liner) melewati akhir program akan membawa Anda kembali ke awal program. Jenis perilaku nonlinear ini berarti Anda dapat menulis kode yang ada di dalam dan di luar string literal pada saat yang sama; sebuah "pembatas yang tak tertandingi (atau apa pun pembatas stringnya) secara efektif akan memberi Anda string yang berisi seluruh sisa program (kecuali untuk pembatas itu "sendiri).
Salah satu masalah dengan menggunakan trik ini adalah Anda akan mendapatkan string seperti yang terlihat dari sudut pandang ", bukan dari awal program (seperti yang Anda inginkan). Karena itu, kemungkinan paling mudah untuk mengatur ulang program sehingga "muncul di awal atau akhir. Ini sering berarti memotong program Anda menjadi beberapa bagian dan memanfaatkan perintah kontrol aliran menarik / tidak biasa apa pun yang dimiliki bahasa Anda (sebagian besar bahasa yang membuat string literal membungkus program memiliki pilihan yang baik).
Yang tak tertandingi "pada akhir program membungkus seluruh program dalam string literal, jadi (berjalan kanan ke kiri karena <pada awal) yang harus kita lakukan adalah meng-output program ( 9k), kemudian mengeluarkan kuotasi ganda ( '!1+,) dan keluar ( @). Ini menghemat membutuhkan dua salinan program (satu sebagai kode, satu sebagai data); kedua salinan itu adalah bagian kode yang sama, hanya ditafsirkan dengan cara yang berbeda.
Kadang-kadang, Anda menggunakan evalatau serupa untuk menghapus duplikasi, tetapi secara umum saya telah menemukan bahwa ini membantu dalam menulis quines sederhana.
Mari kita lihat dua quines Underload berbeda. Ini yang pertama:
(:aSS):aSS
Bagian pertama adalah (:aSS), yang menghasilkan representasi data. Yang kedua adalah :aS, yang mencetak (:aSS). Bagian ketiga adalah S, yang mencetak :aSS.
Ini quine kedua:
(:aS(:^)S):^
Pada awalnya, ini sepertinya tidak cocok. Tetapi jika Anda memperluas quine, Anda mendapatkan:
(:aS(:^)S):aS(:^)S
Sekarang (:aS(:^)S)adalah bagian 1, :aSbagian 2, dan (:^)Sbagian 3.
Manfaatkan pemformatan string
Salah satu cara termudah untuk membuat quine adalah dengan mendefinisikan string, kemudian memasukkan string ke dalam dirinya dengan pemformatan string.
Jadi dalam contoh ini Python quine, kita mendeklarasikan string dengan bagian pertama sama dengan apa pun yang ada sebelum string
s=
, lalu kita membiarkan string dimasukkan dengan format%r
, dan akhirnya kita meletakkan apa yang muncul setelah string untuk mencetak dan memformatnya . Trailing newline adalah karenaprint
mencetak trailing newline.Jadi templatnya benar-benar seperti ini, dengan Python:
Untuk memperluas kuin yang ada dengan lebih banyak kode:
sumber
Fungsi yang ditentukan
Dalam beberapa bahasa, objek fungsi (atau konstruksi yang setara) secara implisit menyimpan kode sumbernya, dan akan mengembalikannya ketika dikonversi menjadi string. Ini memungkinkan compact quines tanpa menggunakan string eval . Contoh penting dari bahasa tersebut adalah JavaScript:
Kode ini mendefinisikan dan memanggil fungsi
f
yang, ketika dipanggil, mencetak kode sumbernya sendiri diikuti oleh panggilan untuk dirinya sendiri. Satu-satunya bagian dari program yang perlu diduplikasi adalah pemanggilan fungsif()
. Tentu saja, badan fungsi dapat menyertakan "payload" kode yang sewenang-wenang yang juga akan dieksekusi ketika fungsi dipanggil.Versi yang lebih ringkas dari trik yang sama berfungsi dalam bahasa golf GolfScript :
dan CJam :
Masing-masing quine ini pertama-tama mendefinisikan suatu blok kode anonim (terlampir dalam kurung kurawal), yang berperilaku seperti objek fungsi dalam JavaScript: ia dapat dieksekusi, dan jika diubah, ia mengembalikan kode sumbernya. Sisa kode (
.~
dalam GolfScript, atau_~
di CJam) kemudian mengeksekusi blok, sambil meninggalkan salinannya di tumpukan. Kode di dalam blok kemudian mendorong string ke tumpukan yang mengulang kode di luar blok. Ketika penerjemah keluar, ia secara otomatis mengencangkan dan mencetak semua yang tersisa di tumpukan. Seperti contoh JavaScript, blok kode ini juga dapat dibuat untuk membawa dan mengeksekusi payload sewenang-wenang dari kode tambahan tanpa harus menduplikatnya.sumber
Gunakan pembatas string yang bersarang tanpa melarikan diri
Seringkali, salah satu bagian tersulit tentang menulis quine adalah langkah melarikan diri. Ini diperlukan di hampir setiap quine; masalahnya adalah Anda menyimpan data entah bagaimana, dan Anda perlu mereplikasi kode yang menyimpan data dalam output quine. Kode itu akan berisi formulir data yang lolos, sehingga program akan melihat formulir yang tidak dihapus, dan Anda harus melarikan diri kembali.
Cara termudah untuk menangani langkah unescaping adalah jika bentuk data yang lolos dan tidak lolos hanya berbeda dalam ada atau tidak adanya pembatas string. Dengan demikian, meloloskan diri merupakan masalah sederhana dengan menambahkan sepasang pembatas string baru di sekitar string. Sayangnya, ini jelas hanya bisa berfungsi jika pembatas string itu sendiri dapat diekspresikan dalam data tanpa melarikan diri.
Perl adalah contoh bahasa di mana trik ini bekerja. Meskipun pembatas string yang biasa adalah
"…"
atau'…'
,q(…)
sarang yang jarang digunakan , memungkinkan jenis quine ini ditulis:Ini adalah kode + data quine.
s///
adalah operasi penggantian string regex; kami menggunakan0
sebagai marker, dan mencocokkannya di dalam regex sebagai\d
("digit apa saja"), untuk menghindari penggunaan marker lebih dari sekali (walaupun sebagai optimasi lain, kami sebenarnya bisa saja menggunakan0
lagi, karena Perls///
hanya menggantikan yang pertama kali muncul dengan default). Perhatikan bahwa tidak ada langkah pelarian yang eksplisit diperlukan di sini, karenaq(…)
pembatas hanya dapat dimasukkan secara harfiah dalam string data.sumber
Kode + kueri data
Struktur paling umum untuk quine terlihat seperti kodesemu ini:
Struktur ini dapat digunakan untuk menulis quine (cukup naif) di sebagian besar bahasa. Namun, skornya cenderung buruk pada sebagian besar sistem penilaian, karena Anda harus menulis keseluruhan program dua kali. Namun, sebagian besar struktur quine dapat dianggap optimalisasi yang satu ini.
Ada beberapa kehalusan untuk ini. Dalam beberapa bahasa, bagian tersulit dalam melakukan operasi ini adalah menulis kode pelolosan; dalam banyak bahasa, membuat penanda tanpa menyebutkan namanya itu sulit; dan dalam beberapa bahasa esoterik, Anda harus menemukan jenis string literal Anda sendiri. Ketiga operasi cenderung tidak menyebabkan terlalu banyak masalah.
Sebagai contoh, kita dapat menulis quine Python yang keluar dari string menggunakan
repr
, dan menggunakan string 2-karakter-urutanx"
(yang direpresentasikan sebagai"x\""
, yaitu tidak menggunakan urutanx"
dalam representasi string dari string itu sendiri) sebagai penanda:sumber
Mengeksploitasi kode sumber pembungkus
Dalam beberapa bahasa (kebanyakan bahasa 2D), kode sumber dapat digunakan; dalam keadaan tertentu (misalnya di Befunge-98, jika program Anda adalah satu-liner) melewati akhir program akan membawa Anda kembali ke awal program. Jenis perilaku nonlinear ini berarti Anda dapat menulis kode yang ada di dalam dan di luar string literal pada saat yang sama; sebuah
"
pembatas yang tak tertandingi (atau apa pun pembatas stringnya) secara efektif akan memberi Anda string yang berisi seluruh sisa program (kecuali untuk pembatas itu"
sendiri).Salah satu masalah dengan menggunakan trik ini adalah Anda akan mendapatkan string seperti yang terlihat dari sudut pandang
"
, bukan dari awal program (seperti yang Anda inginkan). Karena itu, kemungkinan paling mudah untuk mengatur ulang program sehingga"
muncul di awal atau akhir. Ini sering berarti memotong program Anda menjadi beberapa bagian dan memanfaatkan perintah kontrol aliran menarik / tidak biasa apa pun yang dimiliki bahasa Anda (sebagian besar bahasa yang membuat string literal membungkus program memiliki pilihan yang baik).Contoh yang bagus adalah @ Justin's Befunge-98 quine :
Yang tak tertandingi
"
pada akhir program membungkus seluruh program dalam string literal, jadi (berjalan kanan ke kiri karena<
pada awal) yang harus kita lakukan adalah meng-output program (9k
), kemudian mengeluarkan kuotasi ganda ('!1+,
) dan keluar (@
). Ini menghemat membutuhkan dua salinan program (satu sebagai kode, satu sebagai data); kedua salinan itu adalah bagian kode yang sama, hanya ditafsirkan dengan cara yang berbeda.sumber
Ingat Struktur Quine
Saya suka menganggap quines sebagai tiga bagian, daripada 2:
Ini dapat membuatnya lebih mudah untuk memikirkan quines. Berikut adalah quine Python, dengan setiap baris terkait dengan bagian:
Kadang-kadang, Anda menggunakan
eval
atau serupa untuk menghapus duplikasi, tetapi secara umum saya telah menemukan bahwa ini membantu dalam menulis quines sederhana.Mari kita lihat dua quines Underload berbeda. Ini yang pertama:
Bagian pertama adalah
(:aSS)
, yang menghasilkan representasi data. Yang kedua adalah:aS
, yang mencetak(:aSS)
. Bagian ketiga adalahS
, yang mencetak:aSS
.Ini quine kedua:
Pada awalnya, ini sepertinya tidak cocok. Tetapi jika Anda memperluas quine, Anda mendapatkan:
Sekarang
(:aS(:^)S)
adalah bagian 1,:aS
bagian 2, dan(:^)S
bagian 3.sumber