Chuyển đổi biểu thức to thành biểu thức SK


20

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ần xthâ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`xxxysẽ đượ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ự SKxcho 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à , 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


1
Tôi nghĩ trường hợp thử nghiệm thứ hai của bạn là không chính xác. Cái cuối cùng chứa một số không trong ngoặc.
Christian Sievers


Làm thế nào bạn có được λ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?
BarbaraKwarc

@BarbaraKwarc Phải, ý tôi là SIIkhông SKK. Đó là một sai lầm.
Trái cây Esolanging

Câu trả lời:


9

Haskell, 251 237 222 214 byte

15 byte được lưu nhờ vào @ janrjan_Johansen (cũng xem các liên kết TIO của anh ấy trong phần nhận xét)!

Thêm 8 byte được lưu nhờ @nimi!

data E=S|K|E:>E|V Int
p(h:s)|h>'_',(u,a)<-p s,(v,b)<-p u=(v,a:>b)|h>'['=a<$>p s|[(n,_:t)]<-reads s=(t,V n)
a(e:>f)=S:>a e:>a f
a(V 0)=S:>K:>K
a(V n)=K:>V(n-1)
a x=K:>x
o(e:>f)='`':o e++o f
o S="S"
o K="K"
f=o.snd.p

pphân tích cú pháp đầu vào, trả về phần chưa được chỉnh sửa còn lại trong thành phần đầu tiên của cặp kết quả. Ký tự đầu tiên của đối số của nó phải là dấu gạch chéo ngược, dấu gạch chéo ngược hoặc dấu ngoặc mở. Các mẫu bảo vệ pkiểm tra các trường hợp theo thứ tự này. Trong trường hợp đầu tiên, biểu thị một ứng dụng, hai biểu thức nữa được phân tích cú pháp và kết hợp thành một phần tử của Ekiểu dữ liệu với hàm tạo infix :>. Trong trường hợp lambda, biểu thức sau được phân tích cú pháp và ngay lập tức được cung cấp cho ahàm. Mặt khác, nó là một biến, chúng ta lấy số của nó với readshàm (trả về một danh sách) và thả khung đóng theo mẫu khớp với (_:t).

Các achức năng thực hiện trừu tượng khung khá nổi tiếng. Để trừu tượng hóa một ứng dụng, chúng tôi trừu tượng hóa hai biểu thức con và sử dụng Stổ hợp để phân phối đối số cho cả hai. Điều này luôn đúng, nhưng với nhiều mã hơn, chúng ta có thể làm tốt hơn nhiều bằng cách xử lý các trường hợp đặc biệt để có được các biểu thức ngắn hơn. Tóm tắt biến hiện tại cho Ihoặc khi chúng ta không có điều đó , SKK. Thông thường các trường hợp còn lại chỉ có thể thêm a Kvào phía trước, nhưng khi sử dụng ký hiệu này, chúng ta phải đánh số lại các biến khi lambda bên trong được trừu tượng hóa.

obiến kết quả thành một chuỗi cho đầu ra. flà hàm hoàn chỉnh.

Như trong nhiều ngôn ngữ, dấu gạch chéo ngược là một ký tự thoát, do đó, nó phải được đưa ra hai lần trong một chuỗi bằng chữ:

*Main> f "\\[0]"
"``SKK"
*Main> f "\\`[0][0]"
"``S``SKK``SKK"
*Main> f "\\\\[0]"
"``S``S`KS`KK`KK"
*Main> f "\\\\[1]"
"``S`KK``SKK"
*Main> f "\\\\\\``[2][0]`[1][0]"
"``S``S`KS``S``S`KS``S`KK`KS``S``S`KS``S``S`KS``S`KK`KS``S``S`KS``S`KK`KK``S`KK``SKK``S``S`KS``S``S`KS``S`KK`KS``S`KK`KK``S`KK`KK``S``S`KS``S``S`KS``S`KK`KS``S``S`KS``S`KK`KK``S``S`KS`KK`KK``S``S`KS``S``S`KS``S`KK`KS``S`KK`KK``S`KK`KK"

1
1. Trên dòng thứ hai, bạn có thể sử dụng (a,(b,v))<-p<$>p s. 2. Có '\\'thể chỉ là _nếu bạn di chuyển trận đấu cuối cùng.
Ørjan Johansen

1
Trên thực tế, hãy gãi phần đầu tiên: Sẽ ngắn hơn để hoán đổi thứ tự bộ và sử dụng p(_:s)=a<$>p scho dòng (đã di chuyển) '\\'.
Ørjan Johansen

1
Hãy thử trực tuyến! cho phiên bản hiện tại của bạn. Nhân tiện, chỉ có 236 byte, bạn có thể bỏ dòng mới cuối cùng.
Ørjan Johansen

2
@ Challenger5 Tôi nghĩ rằng phần lớn là do haskell dựa trên tính toán lambda, vì vậy những người thành thạo haskell có nhiều khả năng bị thu hút bởi loại câu hỏi này :)
Leo

2
Bạn có thể xác định pbằng một biểu thức duy nhất với ba bảo vệ, sắp xếp lại các trường hợp và thả một cặp (): p(h:s)|h>'_',(u,a)<-p s,(v,b)<-p u=(v,a:>b)|h>'['=a<$>p s|[(n,_:t)]<-reads s=(t,V n).
nimi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.