Verity 0.10, dioptimalkan untuk ukuran kode sumber (1944 byte)
Saya awalnya salah membaca pertanyaan dan menafsirkannya sebagai kode-golf. Itu mungkin yang terbaik, karena jauh lebih mudah untuk menulis quine dengan kode sumber pendek daripada kode objek pendek di bawah batasan dalam pertanyaan; yang membuat pertanyaan itu cukup mudah sehingga saya merasa bisa menghasilkan jawaban yang masuk akal, dan mungkin berfungsi sebagai batu loncatan dalam perjalanan menuju jawaban yang lebih baik. Itu juga mendorong saya untuk menggunakan bahasa tingkat yang lebih tinggi untuk input, yang berarti bahwa saya perlu sedikit mengekspresikan dalam program itu sendiri. Saya tidak membuat Verity sebagai bahasa golf untuk perangkat keras (saya sebenarnya disewa untuk membuatnya beberapa waktu yang lalu dalam konteks yang sama sekali berbeda), tetapi ada sedikit kenangan di sana (misalnya tingkat yang jauh lebih tinggi daripada HDL biasa, dan itu memiliki boilerplate jauh lebih sedikit; ini juga jauh lebih portabel daripada HDL khas).
Saya cukup yakin bahwa solusi yang tepat untuk kode objek pendek melibatkan penyimpanan data dalam beberapa jenis struktur pohon, mengingat bahwa pertanyaannya melarang penggunaan ROM blok, yang biasanya Anda simpan dalam program praktis; Saya mungkin harus menulis program yang menggunakan prinsip ini (tidak yakin bahasa apa, mungkin Verity, mungkin Verilog; VHDL memiliki terlalu banyak boilerplate yang cenderung optimal untuk masalah semacam ini) di beberapa titik. Itu berarti Anda tidak perlu meneruskan setiap bit kode sumber ke setiap bit "ROM yang dibuat secara manual". Namun, kompiler Verity saat ini mensintesis struktur output berdasarkan prioritas dan asosiatif input, yang berarti bahwa itu secara efektif mewakili pointer instruksi (dengan demikian indeks ke tabel pencarian) di unary,
Program itu sendiri:
import <print>new x:=0$1296in(\p.\z.\a.new y:=(-a 5-a 1-a 1-a 2-a 4-a 2-a 3-a 2-a 6-a 2-a 0-a 3-a 0-a 4-a 4-a 7-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 6-a 7-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 4-a 3-a 2-a 7-a 5-a 7-a 0-a 6-a 4-a 4-a 1-a 6-a 2-a 6-a 1-a 7-a 6-a 6-a 5-a 1-a 2-a 2-a 0-a 5-a 0-a 0-a 4-a 2-a 6-a 5-a 0-a 0-a 6-a 3-a 6-a 5-a 0-a 0-a 5-a 0-a 6-a 5-a 2-a 2-a 1-a 1-a 3-a 3-a 0-a 4-a 5-a 3-a 2-a 7-a 5-a 7-a 0-a 5-a 5-a 5-a 1-a 4-a 4-a 3-a 1-a 5-a 5-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 4-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 5-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 4-a 7-a 3-a 6-a 2-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 6-a 3-a 3-a 5-a 1-a 7-a 2-a 6-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 6-a 3-a 1-a 5-a 3-a 7-a 5-a 1-a 2-a 1-a 0-a 4-a 6-a 3-a 5-a 7-a 5-a 7-a 4-a 6-a 5-a 6-a 0-a 3-a 4-a 1-a 1-a 1-a 2-a 2-a 0-a 4-a 3-a 3-a 4-a 1-a 5-a 1-a 0-a 2-a 1-a 1-a 1-a 4-a 5-a 3-a 6-a 7-a 0-a 6-a 0-a 1-a 3-a 2-a 0-a 5-a 4-a 2-a 0-a 4-a 1-a 7-a 7-a 6-a 3-a 7-a 4-a 2-a 0-a 4-a 3-a 6-a 2-a 6-a 3-a 7-a 4-a 2-a 0-a 5-a 4-a 6-a 0-a 7-a 2-a 0-a 1-a 4-a 5-a 3-a 4-a 4-a 4-a 4-a 3-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 3-a 7-a 4-a 2-a 0-a 4-a 4-a 6-a 5-a 6-a 3-a 7-a 5-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 5-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 1-a 5-a 1-a 1-a 0-a 2-a 7-a 2-a 1-a 1-a 0-a 4-a 7-a 2-a 7-a 1-a 5-a 1-a 4-a 2-a 3-a 7-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 6-a 6-a 1-a 5-a 1-a 5-a 4-a 2-a 6-a 2-a 5-a 1-a 2-a 2-a 0-a 3-a 0-a 5-a 1-a 4-a 4-a 3-a 4-a 4-a 4-a 4-a 6-a 6-a 4-a 4-a 4-a 4-a 3-a 6-a 2-a 6-a 1-a 5-a 0-a 5-a 0-a 0-a 0-a 1-a 6-a 5-a 4-a 3-a 2-a 7-a 5-a 7-a 1-a 4-a 4-a 3-a 6-a 7-a 6-a 7-a 3-a 6-a 2-a 0-a 0-a 1-a 4-a 7-a 4-a 7-a 1-a 6-a 2-a 6-a 1-a 7-a 3-a 6-a 3-a 7-a 0-a 6-a 1-a 5-!x)in while!x>0do(p(if z<32then z+92else z);if z==45then while!y>0do(p 97;p 32;p(48^!y$$3$$32);p 45;y:=!y>>3)else skip;x:=!x>>6))print(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
Lebih mudah dibaca:
import <print>
new x := 0$1296 in
(\p.\z.\a.
new y := (-a 5-a 1-
# a ton of calls to a() omitted...
-a 1-a 5-!x) in
while !x>0 do (
p(if z<32 then z+92 else z);
if z==45
then while !y>0 do (
p 97;
p 32;
p(48^!y$$3$$32);
p 45;
y:=!y>>3 )
else skip;
x:=!x>>6
)
)(print)(!x$$6$$32)(\d.x:=!x>>3^d<<1293;0)
Ide dasarnya adalah bahwa kita menyimpan seluruh data dalam variabel x
. (Seperti biasa untuk quine, kami memiliki bagian kode dan bagian data; data menyandikan teks kode, dan juga dapat digunakan untuk membuat ulang teks data.) Sayangnya, Verity saat ini tidak mengizinkan sangat besar konstanta ditulis dalam kode sumber (menggunakan integer OCaml selama kompilasi untuk mewakili integer dalam sumber, yang jelas tidak benar dalam bahasa yang mendukung tipe integer lebar sewenang-wenang) - dan selain itu, konstanta tidak memungkinkan konstanta menjadi ditentukan dalam oktal - jadi kami menghasilkan nilai x
saat runtime melalui panggilan berulang ke suatu fungsia
. Kami dapat membuat fungsi void dan menyebutnya berulang kali sebagai pernyataan terpisah, tetapi itu akan menyulitkan untuk mengidentifikasi di mana mulai mengeluarkan teks dari bagian data. Jadi sebagai gantinya, saya membuat a
mengembalikan bilangan bulat, dan menggunakan aritmatika untuk menyimpan data (Verity menjamin bahwa aritmatika mengevaluasi dari kiri ke kanan). Bagian data dikodekan x
menggunakan -
tanda tunggal ; ketika ini ditemui pada saat run time, itu diperluas ke penuh -a 5-a 1-
, dll, melalui penggunaan y
.
Menginisialisasi y
sebagai salinan x
cukup halus di sini. Karena a
mengembalikan nol secara khusus, sebagian besar jumlahnya hanya nol minus nol minus ... dan membatalkannya sendiri. Kita akhiri dengan !x
(yaitu "nilai x
"; di Verity, seperti pada OCaml, nama variabel bekerja lebih seperti penunjuk daripada yang lainnya, dan Anda harus melakukan dereferensi secara eksplisit untuk mendapatkan nilai variabel). Aturan Verity untuk unary minus sedikit rumit - minus unary v
ditulis sebagai (-v)
- dengan demikian (-0-0-0-!x)
diuraikan sebagai (-(0-0-0-!x))
, yang sama dengan !x
, dan kami akhirnya menginisialisasi y
sebagai salinan x
. (Perlu juga dicatat bahwa Verity tidakcall-by-value, tetapi lebih memungkinkan fungsi dan operator untuk memilih urutan mereka mengevaluasi sesuatu; -
akan mengevaluasi argumen kiri sebelum argumen kanan, dan khususnya, jika argumen kiri memiliki efek samping, mereka akan terlihat ketika argumen kanan dievaluasi.)
Setiap karakter kode sumber direpresentasikan menggunakan dua digit oktal. Ini berarti bahwa kode sumber terbatas pada 64 karakter yang berbeda, jadi saya harus membuat codepage saya sendiri untuk penggunaan internal. Keluaran dalam ASCII, jadi saya perlu mengkonversi secara internal; ini untuk apa (if z<32 then z+92 else z)
. Inilah rangkaian karakter yang saya gunakan dalam representasi internal, dalam urutan numerik (mis. \
Memiliki codepoint 0, ?
memiliki codepoint 63):
\]^_`abcdefghijklmnopqrstuvwxyz{ !"#$%&'()*+,-./0123456789:;<=>?
Kumpulan karakter ini memberi kita sebagian besar karakter yang penting untuk Verity. Karakter yang hilang adalah }
(artinya kita tidak dapat membuat blok menggunakan {}
, tetapi untungnya semua pernyataan adalah ekspresi sehingga kita dapat menggunakannya ()
sebagai gantinya); dan |
(inilah sebabnya saya harus menggunakan yang eksklusif daripada inklusif ATAU ketika membuat nilai x
, artinya saya perlu menginisialisasi ke 0; namun, saya perlu menentukan seberapa besar ukurannya). Beberapa karakter penting yang ingin saya pastikan ada dalam rangkaian karakter adalah <>
(untuk impor, juga bergeser), ()
(sangat sulit untuk menulis program yang dapat diuraikan tanpa ini), $
(untuk segala sesuatu yang berkaitan dengan bitwidth), dan \
( untuk lambdas, secara teori kita bisa mengatasi inilet…in
tetapi akan jauh lebih verbose).
Untuk membuat program sedikit lebih pendek, saya membuat singkatan untuk print
dan untuk !x$$6$$32
(yaitu, "6 bit terbawah !x
, dilemparkan agar dapat digunakan ke print
perpustakaan) melalui mengikat sementara mereka ke argumen lambda.
Akhirnya, ada masalah output. Verity menyediakan print
perpustakaan yang dimaksudkan untuk hasil debug. Pada simulator, ini mencetak kode ASCII ke output standar, yang dapat digunakan untuk menguji program. Pada papan sirkuit fisik, itu tergantung pada print
perpustakaan yang telah ditulis untuk chip tertentu dan papan di sekitarnya; ada print
perpustakaan di distribusi Verity untuk papan evaluasi yang saya punya akses untuk mencetak output pada tampilan tujuh-segmen. Mengingat bahwa perpustakaan pada akhirnya akan mengambil ruang pada papan sirkuit yang dihasilkan, mungkin ada baiknya menggunakan bahasa yang berbeda untuk solusi yang dioptimalkan untuk masalah ini sehingga kami dapat menampilkan bit-bit output langsung pada kabel.
Omong-omong, program ini adalah O (n²) pada perangkat keras, artinya jauh lebih buruk pada simulator (saya curiga O (n⁴); meskipun tidak yakin, tetapi cukup sulit untuk mensimulasikan bahwa sepertinya tidak mungkin bahkan kubik , dan berdasarkan pada bagaimana waktu bereaksi terhadap perubahan saya ketika saya menulis program, fungsinya tampaknya berkembang sangat cepat). Kompilator Verity membutuhkan 436 pass optimasi (yang jauh, jauh lebih banyak daripada biasanya) untuk mengoptimalkan program, dan bahkan setelah itu, mensimulasikan itu sangat sulit untuk laptop saya. Proses kompilasi-dan-simulasi lengkap membutuhkan waktu berikut:
real 112m6.096s
user 105m25.136s
sys 0m14.080s
dan memuncak pada 2740232 kibiby memori. Program ini membutuhkan total siklus 213646 untuk dijalankan. Tapi itu berhasil!
Bagaimanapun, jawaban ini tidak benar-benar memenuhi pertanyaan karena saya mengoptimalkan untuk hal yang salah, tetapi melihat karena belum ada jawaban lain, ini adalah yang terbaik secara default (dan itu menyenangkan untuk melihat seperti apa palu golf akan terlihat seperti di bahasa perangkat keras). Saat ini saya tidak yakin apakah saya akan bekerja pada suatu program yang bertujuan untuk menghasilkan ouptut yang lebih optimal pada chip. (Kemungkinan akan jauh lebih besar dalam hal sumber, karena O (n) pengkodean data akan lebih kompleks daripada yang terlihat di sini.)