Apa tips umum yang Anda miliki untuk bermain golf di Prolog? Saya mencari ide yang dapat diterapkan pada masalah kode golf secara umum yang setidaknya agak spesifik untuk Prolog (mis. Satu variabel huruf tidak spesifik untuk Prolog untuk mengurangi ukuran program).
Harap tunjukkan dalam tip Anda jika itu khusus untuk implementasi Prolog (mis. SWI-Prolog khusus bawaan)
Harap posting hanya satu tip per jawaban, atau daftar tip yang semuanya terkait erat dengan ide utama yang sama.
prolog
tag agak sia-sia. Kecuali jika kita memiliki tantangan Menafsirkan Prolog, kita tidak membutuhkannya.Jawaban:
Gunakan Operator untuk Nama Predikat
Dimungkinkan untuk memberikan predikat operator sebagai nama selama operator merupakan salah satu operator yang telah ditentukan (tercantum di sini ) dan belum didefinisikan sebagai predikat. Ini menghemat beberapa byte baik ketika mendefinisikan dan memanggil predikat karena predikat operator tidak perlu ditulis dalam
name(arg1,arg2,etc..)
bentuk normal dan dapat dipanggil seperti yang diharapkan oleh operator.Untuk satu dan dua predikat argumen, mereka dapat memberikan nama masing-masing operator unary dan binary. Untuk predikat arity yang lebih tinggi, kita masih bisa menghindari tanda kurung menggunakan pencocokan pola. Sebagai contoh jika kita memiliki predikat
A+B+C:-...
, Prolog akan menggunakan aturan presedensi dan asosiatif operator untuk mengubahnya menjadi(A+B)+C:-...
predikat operator di mana argumen pertama cocok dengan polaA+B
. AtauA-B+C*D:-...
yang menjadi(A-B)+(C*D)
argumen pertama adalah cocok dengan polaA-B
dan yang kedua adalah cocok dengan polaC*D
.Contohnya
Outputnya adalah
X = 5.
Cobalah secara Online!
Akibat wajar
Karena DCG adalah gula sintaksis untuk predikat, mereka juga dapat diberi nama operator. Ini berfungsi seperti yang diharapkan ketika memanggil mereka sebagai DCG baik dari DCG atau menggunakan
phrase
predikat atau orang lain yang dirancang untuk bekerja dengan DCG. Ketika memanggil mereka sebagai tanda kurung diperlukan (mis.A+B-->...
Harus dipanggil seperti+(A,B,...)
) karena predikat DCG mengambil dua argumen tambahan untuk daftar perbedaan mereka. Untuk operator yang bernama DCG dengan lebih dari dua argumen menggunakan pencocokan pola operator maka penting untuk memastikan saat memanggilnya sebagai predikat bahwa pola yang cocok operator didistribusikan dengan benar.Memberi nama operator ke DCG yang tidak menggunakan argumen tambahan dapat berguna jika Anda perlu memanggilnya dalam program Anda sejak saat itu sehingga Anda dapat melakukannya tanpa menggunakan tanda kurung. Perhatian diperlukan karena bisa jadi apa yang Anda simpan dalam tanda kurung Anda dapat kehilangan karena penambahan jarak yang diperlukan untuk mengurai operator yang berdekatan.
Contohnya
Output akan menjadi
Cobalah online!
Peringatan
Dengan operator unary
+
dan-
, Prolog akan mengartikan+20
atau-20
sebagai nomor alih-alih panggilan ke+/1
atau-/1
predikat. Predikat yang diberikan unary+
atau-
sebagai nama masih dapat dipanggil nomor dengan menggunakan tanda kurung (+(20)
,-(20)
). Jika menghindari byte ekstra dari kurung diinginkan operator unary lain seperti\
,$
, dll dapat digunakan sebagai nama sebagai gantinya.Kombinasi predikat pencocokan pola dan nama operator tidak sepenuhnya tanpa cacat. Jika Anda memiliki dua predikat yang memiliki operator yang sama dengan nama mereka dan dengan pencocokan pola satu secara umum lebih umum daripada yang lain maka yang lebih umum dapat dipanggil terlebih dahulu atau jika yang kurang umum gagal (tergantung pada urutan mereka pada sumbernya) . Misalnya dalam contoh di atas jika
A-B+C*D
gagal mencocokkan inputnya maka Prolog akan mencoba meneleponX+Y
. Ini akan menghasilkan kesalahan karenalength/2
mengharuskanY
menjadi integer yang tidak akan karena akan dalam bentukC*D
. Ini dapat dihindari hanya dengan memastikan tidak ada dua predikat yang memiliki operator yang sama dengan nama mereka atau jika gagal maka gunakan pemotongan dan pemesanan sumber dengan hati-hati.sumber
Cobalah untuk menempatkan setiap kasus yang mungkin menjadi satu aturan
Cara bersih untuk memprogram dalam Prolog adalah dengan mendeklarasikan beberapa aturan untuk predikat yang sama. Misalnya, predikat untuk membalik daftar dengan akumulator akan terlihat seperti ini:
Di Code-golf, kita bisa menghapus aturan pertama, dan menambahkan a
;
di akhir aturan kedua untuk kode akhir rekursi:Kita tahu bahwa kondisi pertama
r(T,[H|Z],R)
akan gagal jika T kosong, yaitu jika rekursi harus berakhir dan dengan demikian kita dapat menambahkan penghentian kami sebagai atau klausa setelahnya.Prinsip yang sama berlaku di banyak situasi. Namun perlu dicatat bahwa kadang-kadang sebenarnya lebih pendek untuk mendeklarasikan aturan lain daripada melakukan ini.
sumber
Salah satu trik yang sering berguna: Gunakan batasan CLP (FD) untuk aritmatika integer untuk mendapatkan predikat yang dapat digunakan secara otomatis dalam beberapa arah, sehingga menghindari kondisi dan cabang serta varian khusus.
Gunakan B-Prolog atau GNU Prolog, di mana kendala seperti itu tersedia di luar kotak, tanpa perlu memuat pustaka apa pun.
sumber
library(clpfd)
akan tersedia sebagai perpustakaan yang dimuat sebelumnya atau setidaknya dimuat secara otomatis juga di SWI-Prolog. Mungkin perlu beberapa tahun hingga aritmatika deklaratif dipahami dan dihargai sepenuhnya oleh semua pengguna, yang saat ini telah mengumpulkan pengalaman puluhan tahun dengan fitur-fitur tingkat rendah yang sudah ketinggalan zaman. Semakin banyak Anda menggunakan dan menganjurkan CLP (FD), semakin cepat kita akan mendapatkannya secara default. Sampai saat itu, Anda cukup memasukkannya ke:- use_module(library(clpfd)).
dalam~/.swiplrc
dan hanya menyatakan bahwa Anda menggunakan bahwa "varian" dari SWI-Prolog.Gunakan operator aritmatika sebagai tuple konstruktor dan pasangan kontra
Jika Anda perlu melewati struktur tunggal yang terdiri dari dua atau lebih nilai, hal yang paling jelas untuk digunakan adalah daftar, misalnya
[A,B]
. Itu benar-benar bertele-tele.Ada sebuah alternatif. Nilai prolog dapat menyimpan struktur bersarang yang cukup banyak, yang tidak dievaluasi. Berikut ini contoh yang menunjukkan cara kerjanya:
member(A,B)
hanya tuple bernama dalam situasi ini, dan luarmember
(yang merupakan panggilan fungsi) memperlakukannya seperti itu.Meskipun tuple bernama cukup berguna dalam pemrograman Prolog non-golf, tuple ini mungkin tampak lebih bertele-tele daripada pendekatan daftar. Namun, kita dapat menggunakan cukup banyak karakter arbitrer atas nama konstruktor tuple (dengan asumsi mereka dikutip dengan benar); alih-alih sesuatu yang lucu
member
atau seperti karakter tunggala
, kita dapat melakukan sesuatu seperti ini:Di sini, konstruktor tuple kami adalah
'-'
dan'/'
. Dan menarik untuk dicatat apa yang dilakukan oleh printer cantik itu; itu menggunakan notasi infiks untuk tupel. Ini benar-benar singkat, dan mem-parsing dengan cara yang sama seperti operasi aritmatika yang sebanding. (Ini juga menjelaskan mengapa penggunaan aritmatikais
tidak=
;A = 1+2
akan menyatuA
dengan tuple'+'(1,2)
, sehingga sintaks yang terpisah diperlukan untuk benar-benar mengevaluasi ekspresi aritmatika yang tidak dievaluasi.) Karena konstruktor tuple harus disebut sesuatu , Anda juga dapat menggunakan karakter yang memiliki karakter pendek. sintaks (dan sebagai bonus, dan adalah beberapa pilihan yang paling umum dalam kode non-golf. juga ketika mereka menginginkan pembangun tuple tuple cepat daripada sesuatu yang bermakna, dengan cara yang hampir sama-
/
i
sering digunakan sebagai variabel loop, jadi mereka sepenuhnya masuk akal untuk digunakan dalam input dan output Anda jika Anda ingin tuple di sana karena beberapa alasan).'-'
dan'/'
merupakan pilihan yang baik untuk konstruktor tuple karena konstruktornya berperilaku baik dan bermanfaat, memungkinkan Anda untuk menulis literal tuple dengan singkat. Namun, perhatikan bahwa Anda tidak perlu khawatir tentang prioritas ketika nilai-nilai antara diproduksi di dalam program. Prolog menyimpan tupel yang disimpan sebagai pohon alih-alih sebagai kode sumber, dan printer cantik dapat menampilkannya dengan jelas:Karena sintaks tuple begitu singkat (
f(A,B)
tidak lebih pendek darif(A-B)
), Anda dapat mengganti beberapa argumen predikat dengan tupel tanpa biaya, yang berarti bahwa jika predikat perlu meneruskan dua atau lebih argumennya ke predikat lain, Anda sering dapat membentuknya menjadi tuple dan hanya lulus tuple (meskipun ini akan membutuhkan perubahan semua panggilan ke predikat, selain predikat itu sendiri, untuk menggunakan campuran yang sesuai dari konstruktor dan koma tuple).Keuntungan lain dari sintaks ini adalah jika Anda perlu menggunakan daftar secara internal (daripada untuk beroperasi dengan predikat standar); daftar pada dasarnya hanya satu set sel kontra bersarang, dan sel kontra hanya sebuah tuple dengan konstruktor
'.'
, seperti yang dapat dilihat di sini:Jika kode Anda menggunakan daftar "secara manual", mungkin lebih masuk akal untuk menggunakan konstruktor tuple yang lebih tebal daripada
'.'
. Pilihan umum bagi saya adalah merepresentasikan sel kontra sebagai'/'(Tail,Head)
(karena ini adalah yang paling mudah dibaca yang bisa Anda dapatkan di hasil debug tanpa membuang-buang karakter) Perhatikan bahwa Anda mungkin juga ingin yang[]
setara Anda sendiri ; Anda bisa menggunakannya[]
tetapi panjangnya dua byte, dan ada banyak atom satu byte (semua huruf kecil) yang bisa Anda gunakan.Jadi misalnya, daftar berikut:
dapat dikonversi menjadi representasi manual dalam jumlah karakter yang sama seperti ini:
sementara mendapatkan keuntungan yang
[H|T]
cocok dengan pola-gaya sekarang dapat ditulis lebih singkatT/H
, dan ujian terhadap daftar kosong hanya sebagaix
bukan lebih lama[]
. (Tentu saja, ini datang dengan kelemahan jelas bahwamember
,append
, dll, tidak akan bekerja pada representasi ini.)sumber
-
dan/
. Ini sudah atom-atom normal..
, asalkan karakter berikut bukan%
tata letak atau.Sintaks yang lebih pendek untuk daftar daftar dan cara untuk mendeklarasikan peta
Anda dapat menyimpan byte pada daftar daftar. Jika Anda memiliki daftar
[[1,2],[3,4]]
, Anda sebenarnya dapat mendeklarasikannya sebagai[1:2,3:4]
, yang menyimpan 4 tanda kurung = 4 byte. Perhatikan bahwa Anda dapat menggunakan hal lain selain:
(misalnya,^
).1:2
sebenarnya bukan daftar dalam hal itu (padahal[1,2]
dulu), itu direpresentasikan secara internal sebagai:(1,2)
. Oleh karena itu Anda tidak dapat menggunakan predikat yang berfungsi pada daftar pada sublists yang menggunakan titik dua.Trik ini terutama digunakan untuk mendeklarasikan peta, yaitu daftar kunci dengan nilai yang dilampirkan padanya. Misalnya, jika Anda ingin mendeklarasikan peta
M
yang berisi ejaan digit dalam bahasa Inggris dan Prancis, Anda bisa melakukan sesuatu seperti ini:Misalnya, Anda dapat mengambil elemen peta dengan predikat bawaan
member/2
. Misalnya, jika Anda ingin angka dan kata bahasa Inggris yang sesuai dengan'Quatre'
diM
, Anda bisa melakukan:sumber
[[1,2],[3,4]]
as1*2+3*4
,+(*(1,2),*(3,4))
dan yang dengan demikian juga hanya menggunakan satu byte di mana Anda akan memerlukan dua, untuk membuka dan menutup tanda kurung."abc"
sebagai pengganti[a,b,c]
Satu trik yang rapi: Ketika Anda perlu gagal , gunakan sesuatu yang setara dengan false / 0 , tetapi lebih pendek, seperti:
sumber
\+!
cara aneh 3 byte untuk gagal yang sebenarnya tidak memicu pemangkasan!
(lihat ini untuk alasannya ). Saya rasa tidak mungkin gagal dalam waktu kurang dari 3 byte.\+!
menempel ke kiri ke karakter grafis lain, sedangkan0=1
menempel ke kiri untuk nama.Gunakan kembali predikat dengan berbagai mode panggilan
Misalnya, Anda bisa menguraikan dan mencetak struktur dengan predikat yang sama, sekali dengan argumen variabel dan lain kali dengan istilah dasar. Saya menggunakan pendekatan ini dalam Make the Snake Ciuman Melar . Ini tidak mungkin dalam semua tantangan, tentu saja.
sumber