GNU Prolog, 98 byte
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
Jawaban ini adalah contoh yang bagus tentang bagaimana Prolog dapat berjuang dengan format I / O yang paling sederhana. Ia bekerja dengan gaya Prolog yang sebenarnya melalui menjelaskan masalah, dan bukan algoritma untuk menyelesaikannya: ia menentukan apa yang dianggap sebagai pengaturan gelembung legal, meminta Prolog untuk menghasilkan semua pengaturan gelembung tersebut, dan kemudian menghitungnya. Generasi mengambil 55 karakter (dua baris pertama dari program). Penghitungan dan I / O mengambil 43 lainnya (baris ketiga, dan baris baru yang memisahkan dua bagian). Saya yakin ini bukan masalah yang diharapkan OP menyebabkan bahasa bergulat dengan I / O! (Catatan: Penyorotan sintaksis Stack Exchange membuat ini lebih sulit untuk dibaca, tidak mudah, jadi saya mematikannya).
Penjelasan
Mari kita mulai dengan versi pseudocode dari program serupa yang sebenarnya tidak berfungsi:
b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
and sum(BubbleCounts,InteriorCount)
and Count is InteriorCount + 1
and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Seharusnya cukup jelas cara b
kerjanya: kami merepresentasikan gelembung melalui daftar yang diurutkan (yang merupakan implementasi sederhana dari multiset yang menyebabkan multiset yang sama untuk dibandingkan dengan yang sama), dan satu gelembung []
memiliki hitungan 1, dengan gelembung yang lebih besar memiliki hitungan sama dengan jumlah total gelembung di dalam ditambah 1. Untuk hitungan 4, program ini akan (jika berhasil) menghasilkan daftar berikut:
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
Program ini tidak cocok sebagai jawaban karena beberapa alasan, tetapi yang paling mendesak adalah bahwa Prolog sebenarnya tidak memiliki map
predikat (dan menulisnya akan membutuhkan terlalu banyak byte). Jadi alih-alih, kami menulis program lebih seperti ini:
b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
and b(Tail,TailCount)
and Count is HeadCount + TailCount + 1
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Masalah utama lainnya di sini adalah bahwa ia akan masuk ke loop tak terbatas ketika dijalankan, karena cara urutan evaluasi Prolog bekerja. Namun, kita dapat menyelesaikan loop tak terbatas dengan mengatur ulang program sedikit:
b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
and b(Head,HeadCount)
and b(Tail,TailCount)
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Ini mungkin terlihat cukup aneh - kita menambahkan hitungan sebelum kita tahu apa itu - tetapi GNU Prolog #=
mampu menangani aritmatika nonkausal semacam itu, dan karena itu adalah baris pertama b
, dan HeadCount
dan dan TailCount
keduanya harus kurang dari Count
(yang dikenal), ini berfungsi sebagai metode membatasi secara alami berapa kali istilah rekursif dapat cocok, dan dengan demikian menyebabkan program selalu berakhir.
Langkah selanjutnya adalah sedikit menurunkan golf ini. Menghapus spasi, menggunakan nama variabel karakter tunggal, menggunakan singkatan seperti :-
untuk if
dan ,
untuk and
, menggunakan setof
daripada listof
(memiliki nama yang lebih pendek dan menghasilkan hasil yang sama dalam kasus ini), dan menggunakan sort0(X,X)
daripada is_sorted(X)
(karena is_sorted
sebenarnya bukan fungsi sebenarnya, Saya mengarangnya):
b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).
Ini cukup singkat, tetapi dimungkinkan untuk melakukan yang lebih baik. Wawasan kunci adalah yang [H|T]
benar-benar bertele-tele saat sintaksis daftar berjalan. Seperti yang akan diketahui oleh programmer Lisp, daftar pada dasarnya hanya terbuat dari sel kontra, yang pada dasarnya hanya tupel, dan hampir tidak ada bagian dari program ini yang menggunakan daftar bawaan. Prolog memiliki beberapa sintaks tuple yang sangat pendek (favorit saya adalah A-B
, tetapi favorit kedua saya A/B
, yang saya gunakan di sini karena menghasilkan output debug yang lebih mudah dibaca dalam kasus ini); dan kita juga dapat memilih karakter tunggal kita sendiri nil
untuk bagian akhir daftar, daripada terjebak dengan dua karakter []
(saya memilih x
, tetapi pada dasarnya apa pun berfungsi). Jadi, bukannya[H|T]
, kita bisa menggunakan T/H
, dan mendapatkan output darib
yang terlihat seperti ini (perhatikan bahwa urutan pengurutan pada tupel sedikit berbeda dari yang ada di daftar, jadi ini tidak dalam urutan yang sama seperti di atas):
x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))
Ini agak sulit dibaca daripada daftar bersarang di atas, tetapi itu mungkin; lewati x
s, dan tafsirkan /()
sebagai gelembung (atau sekadar /
gelembung yang merosot tanpa isi, jika tidak ada ()
setelahnya), dan elemen-elemen tersebut memiliki korespondensi 1-ke-1 (jika tidak tertata) dengan versi daftar yang ditunjukkan di atas .
Tentu saja, representasi daftar ini, meskipun jauh lebih pendek, memiliki kelemahan utama; itu tidak dibangun ke dalam bahasa, jadi kami tidak dapat menggunakan sort0
untuk memeriksa apakah daftar kami diurutkan. sort0
bagaimanapun juga cukup verbose, jadi melakukannya dengan tangan bukanlah kerugian besar (pada kenyataannya, melakukannya dengan tangan pada [H|T]
daftar representasi datang dengan jumlah byte yang persis sama). Kunci wawasan di sini adalah bahwa program sebagai cek yang ditulis untuk melihat apakah daftar diurutkan, jika ekornya diurutkan, jika yang ekornya diurutkan, dan sebagainya; ada banyak cek berlebihan, dan kita bisa memanfaatkannya. Sebagai gantinya, kami hanya akan memeriksa untuk memastikan bahwa dua elemen pertama dalam urutan (yang memastikan bahwa daftar akan berakhir diurutkan setelah daftar itu sendiri dan semua sufiksnya diperiksa).
Elemen pertama mudah diakses; itu hanya kepala daftar H
. Elemen kedua agak sulit diakses, dan mungkin tidak ada. Untungnya, x
kurang dari semua tuple yang kami pertimbangkan (melalui operator pembanding umum Prolog @>=
), sehingga kami dapat mempertimbangkan "elemen kedua" dari daftar tunggal x
dan program akan bekerja dengan baik. Sedangkan untuk benar-benar mengakses elemen kedua, metode tersest adalah menambahkan argumen ketiga (argumen keluar) b
, yang kembali x
dalam kasus dasar dan H
dalam kasus rekursif; ini berarti bahwa kita dapat mengambil kepala ekor sebagai output dari panggilan rekursif kedua B
, dan tentu saja kepala ekor adalah elemen kedua daftar. Jadi b
terlihat seperti ini sekarang:
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
Kasing dasar cukup sederhana (daftar kosong, kembalikan hitungan 0, "elemen pertama" daftar kosong adalah x
). Kasus rekursif dimulai dengan cara yang sama seperti sebelumnya (hanya dengan T/H
notasi daripada [H|T]
, dan H
sebagai argumen tambahan); kita mengabaikan argumen tambahan dari panggilan rekursif di kepala, tetapi menyimpannya dalam J
panggilan rekursif di ekor. Maka yang harus kita lakukan adalah memastikan yang H
lebih besar atau sama denganJ
(yaitu "jika daftar memiliki setidaknya dua elemen, yang pertama lebih besar dari atau sama dengan yang kedua) untuk memastikan bahwa daftar akhirnya diurutkan.
Sayangnya, setof
cocok jika kita mencoba menggunakan definisi sebelumnya c
bersama-sama dengan definisi baru ini b
, karena ia memperlakukan parameter yang tidak digunakan dalam cara yang kurang lebih sama dengan SQL GROUP BY
, yang sama sekali bukan yang kita inginkan. Anda dapat mengkonfigurasi ulang untuk melakukan apa yang kita inginkan, tetapi konfigurasi ulang itu membutuhkan karakter. Sebagai gantinya, kami menggunakan findall
, yang memiliki perilaku default yang lebih nyaman dan hanya dua karakter lebih lama, memberi kami definisi c
:
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
Dan itu adalah program yang lengkap; hanya menghasilkan pola gelembung, kemudian menghabiskan seluruh byte untuk menghitungnya (kita perlu waktu yang cukup lama findall
untuk mengonversi generator ke daftar, kemudian sayangnya diberi nama secara verbal length
untuk memeriksa panjang daftar itu, ditambah pelat ketel untuk deklarasi fungsi).