The λ-kalkulus , atau lambda kalkulus, adalah sistem yang logis berdasarkan fungsi anonim. Sebagai contoh, ini ekspresi λ:
λf.(λx.xx)(λx.f(xx))
Namun, untuk keperluan tantangan ini, kami akan menyederhanakan notasi:
- Ubah
λ
ke\
(untuk mempermudah mengetik):\f.(\x.xx)(\x.f(xx))
- The
.
dalam header lambda tidak perlu, sehingga kita bisa menjatuhkannya:\f(\xxx)(\xf(xx))
- Gunakan notasi gaya- Unlambda dengan
`
untuk aplikasi daripada menulis dua fungsi bersama-sama (untuk penjelasan lengkap tentang bagaimana melakukan ini, lihat Mengkonversi antara Notasi Kalkulus Lambda ):\f`\x`xx\x`f`xx
- Ini adalah substitusi yang paling rumit. Ganti setiap variabel dengan angka dalam tanda kurung berdasarkan seberapa dalam bersarang variabel relatif terhadap header lambda miliknya (yaitu menggunakan pengindeksan De Bruijn berbasis 0 ). Misalnya, dalam
\xx
(fungsi identitas),x
di dalam tubuh akan diganti dengan[0]
, karena itu milik header pertama (berbasis 0) yang ditemui ketika melintasi ekspresi dari variabel ke akhir;\x\y``\x`xxxy
akan dikonversi menjadi\x\y``\x`[0][0][1][0]
. Kita sekarang dapat menjatuhkan variabel di header, pergi\\``\`[0][0][1][0]
.
Logika kombinasional pada dasarnya adalah Turing Tarpit yang terbuat dari λ-kalkulus (Ya, sebenarnya, itu yang didahulukan , tapi itu tidak relevan di sini.)
"Logika kombinatori dapat dilihat sebagai varian dari kalkulus lambda, di mana ekspresi lambda (mewakili abstraksi fungsional) digantikan oleh seperangkat kombinator terbatas, fungsi primitif dari mana variabel terikat tidak ada." 1
Tipe paling umum dari logika kombinatorik adalah kalkulus kombinator SK , yang menggunakan primitif berikut:
K = λx.λy.x
S = λx.λy.λz.xz(yz)
Terkadang kombinator I = λx.x
ditambahkan, tetapi berlebihan, karena SKK
(atau memang SKx
untuk apa pun x
) setara dengan I
.
Yang Anda butuhkan adalah K, S, dan aplikasi untuk dapat menyandikan ekspresi apa pun dalam kalkulus λ. Sebagai contoh, berikut ini adalah terjemahan dari fungsi λf.(λx.xx)(λx.f(xx))
ke logika kombinasi:
λf.(λx.xx)(λx.f(xx)) = S(K(λx.xx))(λf.λx.f(xx))
λx.f(xx) = S(Kf)(S(SKK)(SKK))
λf.λx.f(xx) = λf.S(Kf)(S(SKK)(SKK))
λf.S(Sf)(S(SKK)(SKK)) = S(λf.S(Sf))(K(S(SKK)(SKK)))
λf.S(Sf) = S(KS)S
λf.λx.f(xx) = S(S(KS)S)(K(S(SKK)(SKK)))
λx.xx = S(SKK)(SKK)
λf.(λx.xx)(λx.f(xx)) = S(K(S(SKK)(SKK)))(S(S(KS)S)(K(S(SKK)(SKK))))
Karena kita menggunakan notasi awalan, ini ```S`K``S``SKK``SKK``S``S`KSS`K``SKK`
.
1 Sumber: Wikipedia
Tantangan
Sekarang, Anda mungkin sudah menebak apa itu: Tulis program yang menggunakan ekspresi λ yang valid (dalam notasi yang dijelaskan di atas) sebagai input dan output (atau mengembalikan) fungsi yang sama, ditulis ulang dalam kalkulus SK-combinator. Perhatikan bahwa ada sejumlah cara tak terbatas untuk menulis ulang ini; Anda hanya perlu menampilkan salah satu cara tak terbatas.
Ini adalah kode-golf , sehingga pengiriman terpendek yang valid (diukur dalam byte) menang.
Uji Kasus
Setiap test case menunjukkan satu kemungkinan output. Ekspresi di atas adalah ekspresi setara λ-kalkulus.
λx.x:
\[0] -> ``SKK
λx.xx:
\`[0][0] -> ```SKK``SKK
λx.λy.y:
\\[0] -> `SK
λx.λy.x:
\\[1] -> K
λx.λy.λz.xz(yz):
\\\``[2][0]`[1][0] -> S
λw.w(λx.λy.λz.xz(yz))(λx.λy.x):
\``[0]\\[1]\\\``[2][0]`[1][0] -> ``S``SI`KS`KK
λx.f(xx) = S(Kf)(SKK)
? Bukankah seharusnya begitu λx.f(xx) = S(Kf)(SII) = S(Kf)(S(SKK)(SKK))
? Saat mengkonversi λx.f(xx)
, saya mendapatkan S {λx.f} {λx.xx}
pengurangan yang mana S (Kf) {λx.xx}
dan ekspresi dalam tanda kurung tidak lain adalah ω=λx.xx
, yang kita tahu direpresentasikan sebagai SII = S(SKK)(SKK)
, kan?
SII
, tidak SKK
. Itu adalah sebuah kesalahan.