공통 Lisp, 560 바이트
"마침내에 대한 사용을 찾았습니다 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))))))
언 골프
;; 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))))
베타 감소
을 사용하여 변수를 축소하는 동안 변수가 PROGV
새 공통 리스프 기호 로 동적으로 바인딩됩니다 MAKE-SYMBOL
. 이것은 네이밍 충돌 (예를 들어, 바운드 변수의 원하지 않는 그림자)을 피할 수있게합니다. 을 사용할 수 GENSYM
있었지만 기호에 대해 사용자에게 친숙한 이름을 원합니다. 즉 심볼에서 문자로 명명 된 이유 a에 z(질문에서 허용). N
현재 범위에서 다음으로 사용 가능한 문자의 문자 코드를 나타내며 일명 97로 시작a .
다음은 매크로 R
없이 읽을 수있는 버전입니다 W
.
(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))))))
중간 결과
문자열에서 파싱 :
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))))))
줄이다:
CL-USER> (r *)
(L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|b| #:|a|))))))
(실행 추적 참조)
예쁜 인쇄 :
CL-USER> (o *)
"a.a.a.a.a.b.a"
테스트
파이썬 답변과 동일한 테스트 스위트를 재사용합니다.
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
8 번째 테스트 예는 위 표에 비해 너무 큽니다.
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))
- 편집 나는 aditsu의 대답 과 동일한 그룹화 동작을 갖기 위해 대답을 업데이트했습니다. 쓰는 데 적은 바이트가 있기 때문에 했습니다.
- 나머지 차이는 결과는 시험 6과 8을 위해 볼 수있는
a.a.a.a.a.b.a
정확하고 바인딩하는 파이썬 답변으로 많은 문자로 사용하지 않는 a
, b
, c
및 d
참조되지 않습니다.
공연
위의 7 가지 통과 테스트를 반복하고 결과를 즉시 수집합니다 (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
동일한 테스트를 수백 번 수행하면 특수 변수에 대한 알려진 제한 으로 인해 SBCL에서 "스레드 로컬 스토리지가 소진되었습니다" . CCL을 사용하면 동일한 테스트 스위트를 10000 회 호출하는 데 3.33 초가 걸립니다.