Lisp thông thường, 560 byte
"Cuối cùng, tôi tìm thấy một sử dụng cho PROGV
."
(macrolet((w(S Z G #1=&optional(J Z))`(if(symbolp,S),Z(destructuring-bind(a b #1#c),S(if(eq a'L),G,J)))))(labels((r(S #1#(N 97))(w S(symbol-value s)(let((v(make-symbol(coerce`(,(code-char N))'string))))(progv`(,b,v)`(,v,v)`(L,v,(r c(1+ n)))))(let((F(r a N))(U(r b N)))(w F`(,F,U)(progv`(,b)`(,U)(r c N))))))(p()(do((c()(read-char()()#\)))q u)((eql c #\))u)(setf q(case c(#\S'(L x(L y(L z((x z)(y z))))))(#\K'(L x(L u x)))(#\I'(L a a))(#\((p)))u(if u`(,u,q)q))))(o(S)(w S(symbol-name S)(#2=format()"~A.~A"b(o c))(#2#()"~A(~A)"(o a)(o b)))))(lambda()(o(r(p))))))
Bị đánh cắp
;; Bind S, K and I symbols to their lambda-calculus equivalent.
;;
;; L means lambda, and thus:
;;
;; - (L x S) is variable binding, i.e. "x.S"
;; - (F x) is function application
(define-symbol-macro S '(L x (L y (L z ((x z) (y z))))))
(define-symbol-macro K '(L x (L u x)))
(define-symbol-macro I '(L x x))
;; helper macro: used twice in R and once in O
(defmacro w (S sf lf &optional(af sf))
`(if (symbolp ,S) ,sf
(destructuring-bind(a b &optional c) ,S
(if (eq a 'L)
,lf
,af))))
;; R : beta-reduction
(defun r (S &optional (N 97))
(w S
(symbol-value s)
(let ((v(make-symbol(make-string 1 :initial-element(code-char N)))))
(progv`(,b,v)`(,v,v)
`(L ,v ,(r c (1+ n)))))
(let ((F (r a N))
(U (r b N)))
(w F`(,F,U)(progv`(,b)`(,U)(r c N))))))
;; P : parse from stream to lambda tree
(defun p (&optional (stream *standard-output*))
(loop for c = (read-char stream nil #\))
until (eql c #\))
for q = (case c (#\S S) (#\K K) (#\I I) (#\( (p stream)))
for u = q then `(,u ,q)
finally (return u)))
;; O : output lambda forms as strings
(defun o (S)
(w S
(princ-to-string S)
(format nil "~A.~A" b (o c))
(format nil (w b "(~A~A)" "(~A(~A))") (o a) (o b))))
Giảm beta
Các biến được liên kết động trong quá trình giảm với PROGV
các ký hiệu Lisp chung mới, sử dụng MAKE-SYMBOL
. Điều này cho phép tránh các va chạm đặt tên một cách độc đáo (ví dụ: bóng tối không mong muốn của các biến bị ràng buộc). Tôi có thể đã sử dụng GENSYM
, nhưng chúng tôi muốn có tên thân thiện với người dùng cho các biểu tượng. Đó là lý do tại sao những biểu tượng được đặt tên với chữ từ ađến z(như được cho phép bởi các câu hỏi). N
đại diện cho mã ký tự của chữ cái có sẵn tiếp theo trong phạm vi hiện tại và bắt đầu bằng 97, aka a.
Đây là phiên bản dễ đọc hơn R
(không có W
macro):
(defun beta-reduce (S &optional (N 97))
(if (symbolp s)
(symbol-value s)
(if (eq (car s) 'L)
;; lambda
(let ((v (make-symbol (make-string 1 :initial-element (code-char N)))))
(progv (list (second s) v)(list v v)
`(L ,v ,(beta-reduce (third s) (1+ n)))))
(let ((fn (beta-reduce (first s) N))
(arg (beta-reduce (second s) N)))
(if (and(consp fn)(eq'L(car fn)))
(progv (list (second fn)) (list arg)
(beta-reduce (third fn) N))
`(,fn ,arg))))))
Kết quả trung gian
Phân tích cú pháp từ chuỗi:
CL-USER> (p (make-string-input-stream "K(K(K(KK)))"))
((L X (L U X)) ((L X (L U X)) ((L X (L U X)) ((L X (L U X)) (L X (L U X))))))
Giảm:
CL-USER> (r *)
(L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|b| #:|a|))))))
(Xem dấu vết thực hiện)
In đẹp
CL-USER> (o *)
"a.a.a.a.a.b.a"
Xét nghiệm
Tôi sử dụng lại bộ kiểm tra tương tự như câu trả lời của Python:
Input Output Python output (for comparison)
1. KSK a.b.c.a(c)(b(c)) a.b.c.a(c)(b(c))
2. SII a.a(a) a.a(a)
3. S(K(SI))K a.b.b(a) a.b.b(a)
4. S(S(KS)K)I a.b.a(a(b)) a.b.a(a(b))
5. S(S(KS)K)(S(S(KS)K)I) a.b.a(a(a(b))) a.b.a(a(a(b)))
6. K(K(K(KK))) a.a.a.a.a.b.a a.b.c.d.e.f.e
7. SII(SII) ERROR ERROR
Ví dụ kiểm tra thứ 8 quá lớn so với bảng trên:
8. SS(SS)(SS)
CL a.b.a(b)(c.b(c)(a(b)(c)))(a(b.a(b)(c.b(c)(a(b)(c))))(b))
Python a.b.a(b)(c.b(c)(a(b)(c)))(a(d.a(d)(e.d(e)(a(d)(e))))(b))
- EDIT Tôi đã cập nhật câu trả lời của mình để có hành vi nhóm giống như trong câu trả lời của aditsu , bởi vì nó tốn ít byte hơn để viết.
- Sự khác biệt còn lại có thể được nhìn thấy để thử nghiệm 6 và 8. Kết quả
a.a.a.a.a.b.a
là chính xác và không sử dụng như chữ nhiều như câu trả lời Python, nơi bindings a
, b
, c
và d
không được tham chiếu.
Hiệu suất
Vòng qua 7 bài kiểm tra vượt qua ở trên và thu thập kết quả là ngay lập tức (đầu ra SBCL):
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
310,837 processor cycles
129,792 bytes consed
Thực hiện cùng một bài kiểm tra hàng trăm lần dẫn đến ... "Lưu trữ cục bộ đã cạn kiệt" trên SBCL, do giới hạn đã biết về các biến đặc biệt. Với CCL, việc gọi bộ thử nghiệm tương tự 10000 lần mất 3,33 giây.