Quining a Pristine World

16

Tantangan ini didasarkan dari pertanyaan Helka Homba , Programming a Pristine World . Dari pertanyaan itu, definisi program murni adalah:

Mari kita mendefinisikan program murni sebagai program yang tidak memiliki kesalahan itu sendiri tetapi akan kesalahan jika Anda memodifikasinya dengan menghapus substring N karakter yang berdekatan, di mana 1 <= N < program length.

Misalnya, program tiga karakter Python 2

`8`

adalah program murni ( terima kasih, Sp ) karena semua program yang dihasilkan dari menghapus substring dengan panjang 1 menyebabkan kesalahan (kesalahan sintaks sebenarnya, tetapi semua jenis kesalahan akan dilakukan):

8`
``
`8

dan juga semua program yang dihasilkan dari menghapus substring panjang 2 menyebabkan kesalahan:

`
`

Jika, misalnya, `8telah menjadi program non- kesalahan maka `8`tidak akan murni karena semua hasil penghapusan substring harus kesalahan.

Catatan:

  • Peringatan kompiler tidak dihitung sebagai kesalahan.
  • Subprogram yang error dapat mengambil input atau memberikan output atau melakukan hal lain selama mereka melakukan kesalahan tidak peduli apa akhirnya.

Tugas Anda adalah membuat program yang panjangnya tidak nol yang mencetak kode sumbernya sendiri dengan tepat, mengikuti aturan untuk quine yang tepat , dan masih asli.

Jawaban terpendek dalam byte untuk setiap bahasa menang.

Shelvacu
sumber
Saya menganggap bahasa yang tidak salah tidak dapat bersaing?
ATaco
@ATaco Sayangnya ya. Bahasa lain, seperti lisp, memiliki sintaksis terstruktur sedemikian rupa sehingga membuat program murni yang bermanfaat tidak mungkin.
Shelvacu
RIP Actually / Seriously
ATaco
'Jawaban terpendek dalam byte untuk setiap bahasa menang.' Saya tidak yakin pendek menjadi ukuran terbaik untuk program murni.
P. Siehr
@ P.Siehr Apa yang akan Anda rekomendasikan sebagai gantinya?
Shelvacu

Jawaban:

6

Haskell , 132 byte

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Cobalah online!

Ini adalah perpanjangan dari quine

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

yang bekerja dengan menggabungkan string data dengan versi yang dikutip (dengan menggunakan show) dari dirinya sendiri dan mencetak hasilnya. Namun, ini bukan murni karena karakter apa pun dalam string data dapat dihapus tanpa gagal dan juga bagian $(++)<*>show$atau (++)<*>dapat dihapus tanpa melanggar program.

Untuk memperbaikinya, fungsi cetak khusus qdidefinisikan yang memeriksa panjang string yang diberikan dan memanggil failjika lebih pendek dari 132. Ini menangkap penghapusan urutan apa pun dari string data dan juga penghapusan $(++)<*>show$atau (++)<*>, seperti dalam kedua kasus yang dihasilkan string yang diteruskan ke qlebih pendek.

Di qnomor 132dapat disingkat menjadi 1, 13, 32atau 2, tetapi dalam setiap kasus lagi faildisebut.

Sejauh yang saya tahu, penghapusan substring lainnya menyebabkan kesalahan sintaks atau tipe, sehingga program bahkan tidak dapat dikompilasi. (Sistem tipe ketat Haskell sangat berguna di sini.)

Sunting: Terima kasih kepada Ørjan Johansen dan Shelvacu karena menunjukkan kekurangan!

Laikoni
sumber
Saya khawatir fail[]|length x/=122bisa dihapus. fail[]:[putStr x|length x==122]mungkin bekerja lebih baik.
Ørjan Johansen
Argh, tidak, maka |length x==122bisa dihilangkan. if length x==122 then putStr x else fail[]mungkin?
Ørjan Johansen
@ ØrjanJohansen Tangkapan bagus, saya punya if then elsesebelumnya tapi saya pikir saya bisa mempersingkatnya.
Laikoni
2
putStr xdapat menjadi p x, yang ketika saya mencoba pada sistem saya berjalan untuk waktu yang sangat lama sebelum saya membunuhnya, saya curiga rekursi panggilan ekor dioptimalkan, jadi itu adalah loop tak terbatas. Saya tidak cukup tahu haskell untuk memberikan saran tentang cara memperbaikinya.
Shelvacu
@Shelvacu Whoops. Mengganti nama pmenjadi qharus memperbaikinya.
Ørjan Johansen
4

Python 3 , 113 byte

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

Cobalah online!

Bagaimana itu bekerja

Kami tidak dapat dengan mudah menggunakan banyak pernyataan karena yang kedua dapat dihapus, jadi kami mulai dengan quine ekspresi tunggal:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Untuk melindunginya dari penghapusan substring, kami menggunakan open(1,"w").writesebagai ganti print. Dalam Python 3, writemengembalikan jumlah karakter tertulis, yang akan kami verifikasi adalah 113untuk memastikan bahwa tidak ada bagian dari string yang dihapus. Kami melakukan ini dengan mencari nilai kembali dalam kamus {113:[]}, dan mengulang hasilnya for[]in…:a, yang akan gagal jika kami tidak mendapatkan iterable kosong atau jika forpernyataan dihapus.

Anders Kaseorg
sumber
1
Bisakah Anda memberikan penjelasan tentang cara kerja kode Anda?
Shelvacu
@Shelvacu Ya, ditambahkan.
Anders Kaseorg
3

Ruby, 78 byte

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

Saya menulis ini ketika saya memikirkan tantangan untuk memastikan itu mungkin. Ia menggunakan "pembungkus" yang sama dari salah satu jawaban saya untuk tantangan asli yang asli.

Penjelasan:

  • eval(*[ expr ])

    Ini mengevaluasi kode apa pun yang dikembalikan sebagai program ruby. Ini secara efektif menguji bahwa string yang dikembalikan kode adalah program ruby ​​yang valid. Mudah, program ruby ​​bisa kosong, atau hanya terdiri dari spasi putih.

    Operator "percikan" *memungkinkan Anda menggunakan array sebagai argumen untuk suatu fungsi. Ini juga berarti bahwa jika evaldihapus, program yang dihasilkan adalah (*[ expr ]) , yang bukan ruby ​​yang valid.

  • ($>.write( str )-78).chr

    $> adalah variabel pendek untuk STDOUT.

    $>.write(foo) menulis foo ke STDOUT dan, yang penting untuk kode ini, mengembalikan jumlah byte yang ditulis.

    $>.write(foo)-78: Berikut 78adalah panjang program, dan jika program tidak hancur, juga akan menjadi jumlah byte yang ditulis. Karenanya, dalam kasus tanpa cacat, ini akan mengembalikan nol.

    num.chrmengembalikan num sebagai karakter, misalnya 0.chrakan mengembalikan string yang berisi byte nol tunggal. Dalam program tidak cacat, ini akan memberikan string dengan byte nol tunggal ke eval, yang merupakan program ruby ​​yang valid yang merupakan no-op.

    Selain itu, program dapat menghilangkan substring sedemikian rupa sehingga hanya eval(*[(78).chr])atau eval(*[(8).chr]), yang berarti konstanta numerik tidak dapat diakhiri dengan angka apa pun (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) karena merupakan kode ascii untuk program ruby ​​karakter tunggal yang valid.

  • %{ str }

    Ini adalah sintaks yang kurang dikenal untuk string literal di ruby. Alasan itu digunakan di sini adalah bahwa pasangan seimbang {}dapat digunakan dalam string, yang berarti sintaks ini dapat mengandung dirinya sendiri. Misalnya, %{foo{bar}}sama dengan "foo{bar}".

  • (s=%{ data })%s

    Ini mendefinisikan variabel syang merupakan data quine ini, sebagai string printf.

    Tugas dalam ruby ​​mengembalikan apa yang ditugaskan, jadi ini sama dengan penugasan pertama sdan kemudian berjalans%s

    %pada string adalah gula sintaksis untuk setara dengan ruby ​​dari sprintf. Itu%s menandakan di mana dalam data data itu sendiri harus tertanam.

    Bit kode ini mendefinisikan bagian data quine dan menyematkannya untuk membuat kode lengkap.

Shelvacu
sumber
3

Standar ML (MLton) , 204 182 189 byte

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

Cobalah online!

Untuk MLton, program SML lengkap adalah ekspresi dibatasi dan diakhiri oleh ;(misalnya print"Hello";print"World";) atau deklarasi dengan vardan funkata kunci (misalnya var _=print"Hello"var _=print"World") di mana _kartu liar yang juga dapat diganti dengan nama variabel apa pun.

Opsi pertama tidak berguna untuk pemrograman murni karena ;dengan sendirinya adalah program yang valid (yang tidak melakukan apa-apa, tetapi tidak salah juga). Masalah dengan pendekatan kedua adalah bahwa deklarasi seperti var _=print"Hello"dapat disingkat menjadi hanya var _="Hello"(atau bahkan var _=print) karena deklarasi denganvar karya selama sisi kanan adalah ekspresi atau nilai SML yang valid (SML adalah bahasa fungsional, sehingga fungsi dapat digunakan sebagai nilai juga).

Pada titik ini, saya siap untuk menyatakan pemrograman asli di SML tidak mungkin, ketika kebetulan saya menemukan pencocokan pola di val-deklarasi. Ternyata sintaks untuk deklarasi tidak val <variable_name> = <expression>tetapi val <pattern> = <expression>, di mana pola dapat terdiri dari nama variabel, konstanta dan konstruktor. Karena printfungsi memiliki tipestring -> unit , kita bisa menggunakan pola pertandingan di unit-nilai ()untuk menegakkan bahwa fungsi cetak benar-benar diterapkan ke string: val()=print"Hey". Dengan pendekatan ini, menghapus salah satu printatau "Hey"menghasilkan Pattern and expression disagree-tindakan.

Dengan cara ini mencetak murni, langkah selanjutnya adalah menulis quine, sebelum akhirnya beberapa penjaga perlu ditambahkan. Saya sebelumnya menggunakan teknik quine SML yang mudah (lihat sejarah revisi ), tetapi Anders Kaseorg menunjukkan pendekatan yang berbeda yang dapat menghemat beberapa byte dalam kasusnya. Menggunakan built-inString.toString fungsi bawaan untuk menangani pelolosan string dan merupakan bentuk umum <code>"<data>", di mana "<data>"adalah string pelolosan dari codesebelumnya:

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

Ini adalah quine yang berfungsi tetapi belum asli. Pertama-tama Anders Kaseorg mengetahui bahwa MLton menerima satu kutipan" sebagai kode tanpa menghasilkan kesalahan, yang berarti kita tidak dapat memiliki kode yang diakhiri dengan penawaran seperti di atas. Cara terpendek untuk mencegah hal ini adalah dengan membungkus semuanya setelah val()=dalam tanda kurung, namun kemudian kode dapat dikurangi menjadi val()=(). Cara terpendek kedua yang saya temukan adalah menggunakan val()=hd[ ... ], yaitu kita membungkus semuanya menjadi daftar dan mengembalikan elemen pertamanya untuk membuat pemeriksa tipe senang.

Untuk memastikan bahwa tidak ada bagian dari string data yang dapat dihapus tanpa diketahui, pencocokan pola di val-deklarasikan berguna lagi: Panjang string terakhir yang akan dicetak (dan dengan demikian panjang program) harus sama dengan 195, jadi kita bisa menulis let val t=... val 195=size t in print t enddi badan fnabstraksi bukan print(...). Menghapus bagian dari string menghasilkan panjang kurang dari 189, sehingga menyebabkan Bindpengecualian dilemparkan.

Masih ada masalah yang tersisa: seluruh val 195=size tpemeriksaan bisa dibatalkan. Kami dapat mencegah hal ini dengan memperluas cek agar sesuai dengan tuple:, val t=... val(216,u)=(n+size t,t)in print u endsehingga menghapus hasil cek dalam variabel tidak terikat u.

Secara keseluruhan, ini menghasilkan solusi 195 byte berikut:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

Menerapkan trik golf menggunakan nama variabel operator seperti !, $dan %bukannya n, tdanu untuk menghemat ruang putih (lihat tip ini ) mengarah ke versi 182 byte terakhir.

Semua pemindahan substring lainnya yang tidak secara eksplisit dinyatakan dalam penjelasan harus menghasilkan sintaks atau tipe kesalahan.

Sunting 1: length(explode t) adil size t.
Sunting 2: Terima kasih kepada Anders Kaseorg untuk pendekatan quine yang berbeda dan menunjukkan "kerentanan".

Laikoni
sumber
−2 byte dengan menulis "\""secara langsung dan menggunakan String.toStringuntuk melarikan diri.
Anders Kaseorg
Tunggu, ini mengerikan: MLton tampaknya menerima program ", menghasilkan output kosong ( TIO ).
Anders Kaseorg
@AndersKaseorg Huh, itu aneh. Namun harus diperbaiki untuk menggunakan masalah ini let ... in ... end.
Laikoni
@AndersKaseorg Saya memperbaiki masalah ini, semoga tanpa memperkenalkan "kerentanan" baru.
Laikoni
Sebenarnya saya melihat ke MLton menerima " sebagai program, dan sepertinya bug telah diperbaiki dalam komit ini , jadi mungkin 182 atau 180 saya baik-baik saja selama Anda menentukan versi Git yang belum dirilis dari MLton.
Anders Kaseorg