Tính toán , hay lambda, là một hệ thống logic dựa trên các hàm ẩn danh. Ví dụ: đây là biểu thức::
λf.(λx.xx)(λx.f(xx))
Tuy nhiên, với mục đích của thử thách này, chúng tôi sẽ đơn giản hóa ký hiệu:
- Thay đổi
λ
thành\
(để dễ gõ hơn):\f.(\x.xx)(\x.f(xx))
- Các
.
tiêu đề trong lambda là không cần thiết, vì vậy chúng tôi có thể loại bỏ nó:\f(\xxx)(\xf(xx))
- Sử dụng ký hiệu tiền tố kiểu Unlambda với
`
ứng dụng thay vì viết hai hàm với nhau (để được giải thích đầy đủ về cách thực hiện việc này, hãy xem Chuyển đổi giữa các ký hiệu tính toán Lambda ):\f`\x`xx\x`f`xx
- Đây là sự thay thế phức tạp nhất. Thay thế mỗi biến bằng một số trong ngoặc dựa trên mức độ biến của lồng được liên kết với tiêu đề lambda mà nó thuộc về (nghĩa là sử dụng lập chỉ mục De Bruijn dựa trên 0 ). Ví dụ, trong
\xx
(hàm nhận dạng), phầnx
thân sẽ được thay thế bằng[0]
vì nó thuộc về tiêu đề đầu tiên (dựa trên 0) gặp phải khi đi qua biểu thức từ biến đến cuối;\x\y``\x`xxxy
sẽ được chuyển đổi thành\x\y``\x`[0][0][1][0]
. Bây giờ chúng ta có thể thả các biến trong các tiêu đề, để lại\\``\`[0][0][1][0]
.
Logic kết hợp về cơ bản là một Turing Tarpit được tạo ra từ phép tính ((Chà, thực ra, nó xuất hiện đầu tiên, nhưng điều đó không liên quan ở đây.)
"Logic kết hợp có thể được xem như là một biến thể của phép tính lambda, trong đó các biểu thức lambda (đại diện cho sự trừu tượng hóa chức năng) được thay thế bằng một bộ tổ hợp giới hạn, các hàm nguyên thủy không có biến ràng buộc." 1
Loại logic kết hợp phổ biến nhất là phép tính tổ hợp SK , sử dụng các nguyên hàm sau:
K = λx.λy.x
S = λx.λy.λz.xz(yz)
Đôi khi một tổ hợp I = λx.x
được thêm vào, nhưng nó là dư thừa, vì SKK
(hoặc thực sự SKx
cho bất kỳ x
) là tương đương với I
.
Tất cả những gì bạn cần là K, S và ứng dụng để có thể mã hóa bất kỳ biểu thức nào trong phép tính. Ví dụ, đây là bản dịch từ hàm λf.(λx.xx)(λx.f(xx))
sang logic kết hợp:
λ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))))
Vì chúng tôi đang sử dụng ký hiệu tiền tố, đây là ```S`K``S``SKK``SKK``S``S`KSS`K``SKK`
.
1 Nguồn: Wikipedia
Các thách thức
Đến giờ, có lẽ bạn đã đoán được: Viết chương trình lấy biểu thức valid hợp lệ (theo ký hiệu được mô tả ở trên) làm đầu vào và đầu ra (hoặc trả về) cùng một hàm, được viết lại trong phép tính SK-combinator. Lưu ý rằng có vô số cách để viết lại cái này; bạn chỉ cần xuất ra một trong những cách vô hạn.
Đây là môn đánh gôn , vì vậy bài nộp hợp lệ ngắn nhất (tính bằng byte) sẽ thắng.
Các trường hợp thử nghiệm
Mỗi trường hợp thử nghiệm cho thấy một đầu ra có thể. Biểu thức trên cùng là biểu thức calcul-compus tương đương.
λ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)
? Không nên thay λx.f(xx) = S(Kf)(SII) = S(Kf)(S(SKK)(SKK))
thế? Khi chuyển đổi λx.f(xx)
, tôi nhận được S {λx.f} {λx.xx}
giảm S (Kf) {λx.xx}
và biểu thức trong ngoặc không có gì khác hơn ω=λx.xx
, mà chúng ta biết được biểu diễn dưới dạng SII = S(SKK)(SKK)
, phải không?
SII
không SKK
. Đó là một sai lầm.