Tối ưu hóa trình biên dịch SKI


22

Phép tính SKI là một biến thể của phép tính Lambda không sử dụng biểu thức lambda. Thay vào đó, chỉ có ứng dụng và các tổ hợp S , Ktôi được sử dụng. Trong thử thách này, nhiệm vụ của bạn là dịch các thuật ngữ SKI sang các thuật ngữ Lambda ở dạng β bình thường .


Đặc điểm kỹ thuật đầu vào

Đầu vào là một thuật ngữ SKI trong cách trình bày văn bản sau. Bạn có thể chọn nhận một dòng mới tùy chọn. Các đầu vào bao gồm các nhân vật S, K, I, (, và )và thỏa mãn ngữ pháp sau đây (theo mẫu ABNF) với stermlà biểu tượng bắt đầu:

sterm = sterm combinator     ; application
sterm = combinator           ;
sterm = '(' sterm ')'        ; grouping
combinator = 'S' | 'K' | 'I' ; primitives

Đặc điểm kỹ thuật đầu ra

Đầu ra là một thuật ngữ lambda không có biến miễn phí trong biểu diễn văn bản sau. Bạn có thể chọn đầu ra một dòng mới tùy chọn. Đầu ra phải đáp ứng ngữ pháp sau ở dạng ABNF với ltermký hiệu bắt đầu:

lterm   = lterm operand     ; application
lterm   = ALPHA '.' lterm   ; lambda
lterm   = operand
operand = '(' lterm ')'     ; grouping
operand = ALPHA             ; variable (a letter)

Những ràng buộc

Bạn có thể cho rằng đầu vào có dạng β bình thường. Bạn có thể giả định rằng dạng β bình thường sử dụng tối đa 26 biến khác nhau. Bạn có thể giả sử rằng cả đầu vào và đầu ra đều có thể biểu diễn trong 79 ký tự.

Đầu vào mẫu

Dưới đây là một vài mẫu đầu vào. Đầu ra là một đầu ra có thể cho đầu vào đã cho.

input                        output
I                            a.a
SKK                          a.a
KSK                          a.b.c.ac(bc)
SII                          a.aa

Chấm điểm

Giải pháp ngắn nhất trong octet thắng. Sơ hở thường bị cấm.


7
+1 vì tôi cho rằng đây là một thử thách thú vị; Tôi đã không hiểu một từ của nó.
Alex A.

2
À, tôi nên chơi gôn trượt tuyết của mình.aditsu.net :)
aditsu 16/07/2015

Bạn có thể nên nói rằng cả hai stermltermsử dụng tính kết hợp trái khi dấu ngoặc bị thiếu.
Peter Taylor

@PeterTaylor Cách này tốt hơn?
FUZxxl ngày 16/07/2015

Không, tôi nghĩ điều đó thực sự sai: theo ngữ pháp đã thay đổi tôi sẽ phân tích SKIthành S(KI).
Peter Taylor

Câu trả lời:


3

Haskell , 232 byte

data T s=T{a::T s->T s,(%)::s}
i d=T(i. \x v->d v++'(':x%v++")")d
l f=f`T`\v->v:'.':f(i(\_->[v]))%succ v
b"S"x=l$l.(a.a x<*>).a
b"K"x=l(\_->x)
b"I"x=x
p?'('=l id:p
(p:q:r)?')'=a q p:r
(p:q)?v=a p(l$b[v]):q
((%'a')=<<).foldl(?)[l id]

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

Đây là một trình phân tích cú pháp khác nhau trước câu trả lời của tôi cho Viết Viết một trình thông dịch cho tính toán lambda chưa được đánh dấu , cũng có một phiên bản không được cung cấp với tài liệu.

Tóm lại, Term = T (Char -> String)là loại thuật ngữ tính toán lambda, biết cách tự áp dụng cho các thuật ngữ khác ( a :: Term -> Term -> Term) và cách hiển thị dưới dạng một String( (%) :: Term -> Char -> String), được đưa ra một biến mới ban đầu là a Char. Chúng ta có thể chuyển đổi một hàm theo các thuật ngữ thành một thuật ngữ l :: (Term -> Term) -> Termvà vì ứng dụng của thuật ngữ kết quả chỉ đơn giản gọi hàm ( a (l f) == f), các thuật ngữ được tự động giảm xuống dạng bình thường khi được hiển thị.


9

Ruby, 323 byte

Tôi không thể tin được tác phẩm tào lao này cả:

h={};f=96;z=gets.chop
{?S=>'s0.t0.u0.s0u0(t0u0)',?K=>'k0.l0.k0',?I=>'i0.i0'}.each{|k,v|z.gsub!k,?(+v+?)}
loop{z=~/\((?<V>\w1*0)\.(?<A>(?<B>\w1*0|[^()]|\(\g<B>+\))+)\)(?<X>\g<B>)/
s=$`;t=$';abort z.gsub(/\w1*0/){|x|h[x]=h[x]||(f+=1).chr}if !t
z=$`+?(+$~[?A].gsub($~[?V],$~[?X].gsub(/\w1*0/){|a|s[a]?a:a.gsub(?0,'10')})+?)+t}

Sử dụng thay thế regex để thực hiện giảm on trên chuỗi thô là một số nội dung của Tony-the-Pony. Tuy nhiên, đầu ra của nó có vẻ chính xác ít nhất là cho các thử nghiệm dễ dàng:

$ echo 'I' | ruby ski.rb
(a.a)
$ echo 'SKK' | ruby ski.rb
(a.(a))
$ echo 'KSK' | ruby ski.rb
((a.b.c.ac(bc)))
$ echo 'SII' | ruby ski.rb
(a.(a)((a)))

Đây là cách xử lý K(K(K(KK)))với một số đầu ra gỡ lỗi, mất khoảng 7 giây trên máy tính xách tay của tôi, vì đệ quy biểu thức chính quy chậm . Bạn có thể thấy chuyển đổi α của nó hoạt động!

$ echo 'K(K(K(KK)))' | ruby ski.rb
"(l0.((k10.l10.k10)((k10.l10.k10)((k10.l10.k10)(k10.l10.k10)))))"
"(l0.((l10.((k110.l110.k110)((k110.l110.k110)(k110.l110.k110))))))"
"(l0.((l10.((l110.((k1110.l1110.k1110)(k1110.l1110.k1110)))))))"
"(l0.((l10.((l110.((l1110.(k11110.l11110.k11110))))))))"
(a.((b.((c.((d.(e.f.e))))))))

Tôi nhận được: ski.rb: 4: trong `gsub ': loại sai lập luận bằng không (dự kiến biểu thức chính quy) (TypeError) với 'tôi' dụ
aditsu

Nên sửa ngay! Tôi đã sửa nó tại địa phương, nhưng quên chỉnh sửa bài viết của mình.
Lynn

2
Ok, đó là ........ l ....................... o ........... w, nhưng có vẻ như nó hoạt động .... cuối cùng :) Tôi nghĩ rằng kết quả cho S (K (SI)) K là không chính xác.
aditsu

9

Con trăn 2, 674

exec u"""import sys
$ V#):%=V.c;V.c+=1
 c=97;p!,v,t:[s,t.u({})][v==s];r!:s;u!,d:d.get(s,s);s!:chr(%)
 def m(s,x):%=min(%,x);-(%==x)+x
$ A#,*x):%,&=x
 C='()';p!,x,y:s.__$__(%.p/,&.p/);m!,x:&.m(%.m(x));u!,d:A(%.u(d),&.u(d));s!:%.s()+s.C[0]+&.s()+s.C[1:]
 def r(s):x=%.r();y=&.r();-x.b.p(x.a,y).r()if'L'in`x`else s.__$__/
$ L(A):C='.';u!,d:L(d.setdefault(%,V()),&.u(d))
x=V();y=V();z=V()
I=L(x,x)
K=L(y,L/)
S=L(x,L(z,L(y,A(A/,A(z,y)))))
def E():
 t=I
 while 1:
    q=sys.stdin.read(1)
    if q in')\\n':-t
    t=A(t,eval(max(q,'E()')).u({}))
t=E().r()
t.m(97)
print t.s()""".translate({33:u'=lambda s',35:u':\n def __init__(s',36:u'class',37:u's.a',38:u's.b',45:u'return ',47:u'(x,y)'})

Lưu ý: sau while 1:, 3 dòng được thụt vào với một ký tự tab.

Về cơ bản, đây là mã đằng sau http://ski.aditsu.net/ , được dịch sang python, được đơn giản hóa rất nhiều và được chơi golf nhiều.

Tham khảo: (điều này có lẽ ít hữu ích hơn khi mã được nén)

V = biến hạn
A = hạn ứng dụng
L = lambda hạn
c = biến quầy
p = thay thế biến với hạn
r = giảm
m = biến thức renumbering
u = biến nội renumbering (với các điều kiện trùng lặp)
s = chuỗi chuyển đổi
(tham số s = tự)
C = ký tự phân cách để chuyển đổi chuỗi
I, K, S: tổ hợp
E = parse

Ví dụ:

python ski.py <<< "KSK"
a.b.c.a(c)(b(c))
python ski.py <<< "SII"        
a.a(a)
python ski.py <<< "SS(SS)(SS)"
a.b.a(b)(c.b(c)(a(b)(c)))(a(d.a(d)(e.d(e)(a(d)(e))))(b))
python ski.py <<< "S(K(SI))K" 
a.b.b(a)
python ski.py <<< "S(S(KS)K)I"                   
a.b.a(a(b))
python ski.py <<< "S(S(KS)K)(S(S(KS)K)I)"        
a.b.a(a(a(b)))
python ski.py <<< "K(K(K(KK)))"
a.b.c.d.e.f.e
python ski.py <<< "SII(SII)"
[...]
RuntimeError: maximum recursion depth exceeded

(điều này được mong đợi vì SII(SII)không thể giảm được)

Cảm ơn Mauris và Sp3000 vì đã giúp tiêu diệt một loạt byte :)


1
Tôi chắc rằng bạn có thể biến def m(a,b,c):return foothành m=lambda a,b,c:foothậm chí các lớp bên trong, mà có thể giúp bạn tiết kiệm rất nhiều byte.
Lynn

@Mauris cảm ơn vì tiền boa :)
aditsu

Tôi không đọc được a.b.c.a(c)(b(c))như một biểu thức lambda hợp lệ: là (c)gì?
coredump

@coredump đó là một toán hạng với việc nhóm không cần thiết ... và bạn nói đúng, nó không khớp chính xác với các quy tắc ngữ pháp của OP. Tôi tự hỏi nó quan trọng như thế nào; Tôi sẽ hỏi.
aditsu

@coredump Bây giờ sẽ ổn với ngữ pháp được cập nhật.
aditsu

3

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 PROGVcá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ó Wmacro):

(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.alà 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, cdkhô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.


Đó là một giải pháp gọn gàng!
FUZxxl

@FUZxxl Cảm ơn!
coredump
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.