การเพิ่มประสิทธิภาพคอมไพเลอร์ SKI


22

SKI แคลคูลัสเป็นตัวแปรของแคลคูลัสแลมบ์ดาที่ไม่ได้ใช้การแสดงออกแลมบ์ดา ใช้เฉพาะแอปพลิเคชันและ combinators S , Kและฉันเท่านั้น ในความท้าทายนี้งานของคุณคือการแปลแง่สกีเข้าแง่แลมบ์ดาในรูปแบบปกติβ


ข้อกำหนดการป้อนข้อมูล

อินพุตเป็นคำ SKI ในการแสดงข้อความต่อไปนี้ คุณอาจเลือกที่จะรับบรรทัดใหม่ที่เป็นตัวเลือก การป้อนข้อมูลที่ประกอบด้วยตัวอักษรS, K, I, (และ)และตอบสนองไวยากรณ์ต่อไปนี้ (ในรูปแบบ ABNF) กับstermการเป็นสัญลักษณ์เริ่มต้น:

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

สเปคเอาท์พุท

เอาต์พุตเป็นคำแลมบ์ดาที่ไม่มีตัวแปรอิสระในการแสดงข้อความต่อไปนี้ คุณอาจเลือกที่จะแสดงบรรทัดใหม่ที่เป็นตัวเลือก ผลลัพธ์จะต้องเป็นไปตามหลักไวยากรณ์ต่อไปนี้ในรูปแบบ ABNF โดยltermเป็นสัญลักษณ์เริ่มต้น:

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

ข้อ จำกัด

คุณอาจสันนิษฐานว่าอินพุตมีรูปแบบ. ปกติ คุณอาจสันนิษฐานว่ารูปแบบβปกติใช้ตัวแปรที่แตกต่างกันมากที่สุดถึง 26 รายการ คุณอาจสันนิษฐานได้ว่าทั้งอินพุตและเอาท์พุตมีความยาว 79 อักขระ

อินพุตตัวอย่าง

นี่คือสองตัวอย่างอินพุต เอาต์พุตเป็นเอาต์พุตที่เป็นไปได้หนึ่งรายการสำหรับอินพุตที่กำหนด

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

เกณฑ์การให้คะแนน

ทางออกที่สั้นที่สุดใน octet ชนะ ช่องโหว่ทั่วไปเป็นสิ่งต้องห้าม


7
+1 เพราะฉันคิดว่านี่เป็นความท้าทายที่ยอดเยี่ยม ฉันไม่เข้าใจคำพูดของมัน
Alex A.

2
อ้าฉันควรเล่นสกี ski.aditsu.net :)
aditsu

คุณควรระบุว่าทั้งสองstermและltermใช้การเชื่อมโยงด้านซ้ายเมื่อวงเล็บหายไป
Peter Taylor

@PeterTaylor ดีกว่าด้วยวิธีนี้?
FUZxxl

ไม่ฉันคิดว่าผิดจริง: ต่อไปนี้ที่มีการเปลี่ยนแปลงไวยากรณ์ฉันจะแยกเป็นSKI S(KI)
Peter Taylor

คำตอบ:


3

Haskell , 232 ไบต์

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]

ลองออนไลน์!

มันทำงานอย่างไร

นี่คือส่วนแยกวิเคราะห์หน้าคำตอบของฉันที่แตกต่างไปจาก“ เขียนล่ามสำหรับแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์”ซึ่งมีเวอร์ชั่นที่ไม่ได้บรรจุด้วยเอกสาร

สั้น ๆTerm = T (Char -> String)คือชนิดของคำแคลคูลัสแลมบ์ดาซึ่งรู้วิธีการใช้ตัวเองให้คำอื่น ๆ ( a :: Term -> Term -> Term) และวิธีการแสดงตัวเองเป็นString( (%) :: Term -> Char -> String) Charกำหนดตัวแปรสดเป็นครั้งแรก เราสามารถแปลงฟังก์ชั่นตามเงื่อนไขเป็นคำด้วยl :: (Term -> Term) -> Termและเนื่องจากการใช้งานของคำที่เกิดขึ้นเพียงแค่เรียกฟังก์ชั่น ( a (l f) == f) ข้อตกลงจะลดลงโดยอัตโนมัติในรูปแบบปกติเมื่อแสดง


9

ทับทิม, 323 ไบต์

ฉันไม่อยากจะเชื่อว่าอึของชิ้นนี้ใช้ได้เลย:

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}

การใช้การทดแทน regex เพื่อทำการลดβบนสตริงดิบเป็นบางสิ่งที่ Tony-the-Pony อย่างไรก็ตามผลลัพธ์นั้นถูกต้องอย่างน้อยสำหรับตัวอย่างทดสอบที่ง่าย:

$ 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)))

นี่มันจัดการK(K(K(KK)))กับบางส่วนแก้ปัญหาส่งออกซึ่งจะใช้เวลาประมาณ 7 วินาทีบนแล็ปท็อปของฉันเพราะ recursion แสดงออกปกติเป็นช้า คุณสามารถดูการแปลงαของมันได้!

$ 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))))))))

ฉันได้รับ: ski.rb: 4: ใน `gsub ': อาร์กิวเมนต์ที่ไม่ถูกต้องพิมพ์ศูนย์ (คาดว่า Regexp) (TypeError) พร้อมกับตัวอย่าง' I '
aditsu

ควรได้รับการแก้ไขแล้ว! ฉันแก้ไขแล้วในเครื่อง แต่ลืมแก้ไขโพสต์
Lynn

2
ตกลงมันเป็น ........ l ....................... o ........... w แต่ดูเหมือนว่าจะทำงาน .... ในที่สุด :) ฉันคิดว่าผลลัพธ์สำหรับ S (K (SI)) K นั้นไม่ถูกต้อง
aditsu

9

Python 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)'})

หมายเหตุ: หลัง while 1:นั้น 3 บรรทัดจะถูกย่อหน้าด้วยอักขระแท็บ

นี่เป็นโค้ดที่อยู่เบื้องหลังhttp://ski.aditsu.net/ซึ่งแปลเป็นภาษาไพ ธ อนทำให้ง่ายขึ้นมากและเล่นกอล์ฟอย่างหนัก

การอ้างอิง: (นี่อาจมีประโยชน์น้อยกว่าตอนนี้รหัสถูกบีบอัด)

V = ตัวแปรระยะ
A = ระยะประยุกต์ใช้
L = แลมบ์ดาระยะ
c = เคาน์เตอร์ตัวแปร
p = แทนที่ตัวแปรที่มีระยะเวลา
r = ลด
m = ตัวแปรสุดท้ายจัดเลขระบุฉบับ
U = ตัวแปรภายในจัดเลขระบุฉบับ (สำหรับคำที่ซ้ำกัน)
s = แปลงสตริง
(พารามิเตอร์ s = ตัวเอง)
C = อักขระตัวคั่นสำหรับการแปลงสตริง
I, K, S: combinators
E = parse

ตัวอย่าง:

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

(คาดว่า this นี้เพราะSII(SII)ลดไม่ได้)

ขอบคุณ Mauris และ Sp3000 ที่ช่วยฆ่าไบต์จำนวนมาก :)


1
ฉันค่อนข้างมั่นใจว่าคุณสามารถdef m(a,b,c):return fooเข้าm=lambda a,b,c:fooเรียนได้แม้ในชั้นเรียนซึ่งอาจช่วยให้คุณประหยัดจำนวนไบต์
ลินน์

@Mauris ขอบคุณสำหรับคำแนะนำ :)
aditsu

ฉันล้มเหลวในการอ่านa.b.c.a(c)(b(c))เป็นนิพจน์แลมบ์ดาที่ถูกต้อง: มันคือ(c)อะไร?
coredump

@coredump มันเป็นตัวถูกดำเนินการด้วยการจัดกลุ่มที่ไม่จำเป็น ... และคุณพูดถูกมันไม่ตรงกับกฎไวยากรณ์ของ OP ฉันสงสัยว่ามันสำคัญอย่างไร ฉันจะถาม
aditsu

@coredump มันควรจะใช้ได้ตอนนี้กับไวยากรณ์ที่อัปเดตแล้ว
aditsu

3

เสียงกระเพื่อมสามัญ 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))))))

Ungolfed

;; 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))))

Beta-ลดลง

ตัวแปรที่มีความผูกพันแบบไดนามิกในระหว่างการลดลงที่มีPROGVสัญลักษณ์ใหม่ Common 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"

การทดสอบ

ฉันใช้ชุดการทดสอบเดียวกันกับคำตอบของ 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

ตัวอย่างการทดสอบที่ 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 การเรียกชุดการทดสอบเดียวกัน 10,000 ครั้งใช้เวลา 3.33 วินาที


นั่นเป็นทางออกที่เรียบร้อย!
FUZxxl

@FZZxxl ขอบคุณ!
coredump
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.