Jawaban ini adalah bagian dari kolaborasi antara saya dan 0 '. Kami berdua mengerjakan ini bersama, satu-satunya alasan saya mempostingnya adalah karena saya memenangkan Rock, Paper, Scissors.
\Q-->{Q=1};"(",\N,")",\B,{findnsols(N,I,(between(2,inf,I),\+ (between(3,I,U),0=:=I mod(U-1))),L)->append(_,[Y],L),Q is Y*B}.
Cobalah online!
Penjelasan
Jawaban ini adalah contoh sempurna dari apa yang membuat bermain golf di prolog menyenangkan.
Jawaban ini menggunakan sistem kuat Prolog untuk tata bahasa klausa yang pasti. Ini adalah tata bahasa kita yang tidak diserang sedikit.
head(1)-->[].
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Aturan konstruksi pertama adalah:
head(1)-->[].
Ini memberi tahu Prolog bahwa string kosong berhubungan dengan 1.
Aturan konstruksi kedua kami sedikit lebih kompleks.
head(Q)-->"(",head(N),")",head(B),{prime(N,Y),Q is Y*B}.
Ini memberitahu kita bahwa string yang tidak kosong berisi tanda kurung di sekitar klausa dengan aturan yang sama, di sebelah kanan klausa dengan aturan yang sama.
Ini juga memberi tahu kita bahwa nilai klausa ini ( Q
) mengikuti aturan:
{prime(N,Y),Q is Y*B}
Breaking down ini, Q
adalah produk dari 2 angka Y
dan B
. B
hanya nilai dari klausa ke kiri dan Y
merupakan N
perdana ke manaN
nilai klausa di dalam tanda kurung.
Aturan ini mencakup kedua aturan pembentukan pohon faktor
- Penggabungan digandakan
- Enclosure mengambil nth prime
Sekarang untuk definisi predikat. Dalam versi yang tidak diserang ada dua predikat yang bermain (dalam kode saya yang sebenarnya saya sudah maju merantai predikat itu). Dua predikat yang relevan di sini adalah isprime/1
, yang cocok dengan bilangan prima, dan prime/2
, yang, diberikan N
dan Y
, cocok dengan iff Y
adalah N
bilangan prima. Pertama yang kita miliki
isprime(I):- \+ (between(3,I,U),0 =:= I mod(U-1)).
Ini berfungsi dari definisi standar yang cukup primoral, kami bersikeras bahwa tidak ada angka antara 2 dan I
, termasuk 2 tetapi tidak I
membagi I
.
Predikat berikutnya juga cukup sederhana
prime(N,Y):-
findnsols(N,I,(
between(2,inf,I),
isprime(I)
),L),
append(_,[Y],L),!.
Kami menggunakan findnsols
untuk menemukan N
angka pertama yang prima, kami kemudian mengembalikan yang terakhir. Kuncinya di sini adalah bahwa sementara findnsols
tidak dijamin untuk menemukan bilangan prima terkecil N
, karena cara SWI menangani between
itu akan selalu menemukan bilangan prima yang lebih kecil lebih cepat. Namun ini berarti kita harus memotong untuk mencegahnya menemukan lebih banyak bilangan prima.
Golf
Kami dapat meneruskan alasan dalam kode kami dua kali. Karena isprime
hanya digunakan setelah definisi dapat dipindahkan ke dalamnya prime
. Yang berikutnya adalah untuk pindah prime
langsung ke dalam DCG, namun karena kami menggunakan cut-in prime
untuk mencegah findnsols
dari menghasilkan terlalu banyak bilangan prima, kami memiliki sedikit masalah. Pemotongan, memotong seluruh DCG alih-alih hanya sedikit yang kita inginkan. Setelah sedikit penggalian dokumentasi kami menemukan bahwa once/1
dapat digunakan untuk memotong hanya bagian ini tetapi tidak seluruh DCG. Namun penggalian dokumentasi lebih lanjut mengungkapkan bahwa ->
operator juga dapat digunakan untuk melakukan tugas serupa. The ->
Operator kira-kira setara dengan ,!,
jadi kami pindah cut kami ke sisi lain dari append/3
dan menggantinya dengan ->
.
Dalam SWI-Prolog predikat (dan aturan) dapat memberikan operator sebagai nama yang memungkinkan kita untuk melepaskan tanda kurung yang biasanya diperlukan. Dengan demikian kita dapat menyimpan 6 byte dengan memanggil aturan \
.