SKI 컴파일러 최적화


22

스키 미적분은 람다 표현식을 사용하지 않는 람다 계산법의 변형이다. 대신, 응용 프로그램 및 결합기 S , KI 만 사용됩니다. 이 과제에서 SKI 용어를 β normal 형식의 Lambda 용어로 변환해야합니다 .


입력 사양

입력은 다음 텍스트 표현에서 SKI 용어입니다. 선택적인 후행 줄 바꾸기를 받도록 선택할 수 있습니다. 입력이 문자로 구성되어 S, K, I, (, 및 )와 함께 만족 (ABNF 형태) 다음 문법 sterm시작 심볼 인 :

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

출력 사양

출력은 다음 텍스트 표현에서 자유 변수가없는 람다 용어입니다. 선택적인 후행 줄 바꿈을 출력하도록 선택할 수 있습니다. 출력 lterm은 시작 기호 인 ABNF 형식의 다음 문법을 만족해야합니다 .

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

채점

옥텟 단위로 가장 짧은 솔루션이 이깁니다. 일반적인 허점은 금지되어 있습니다.


7
나는 이것이 멋진 도전이라고 가정하기 때문에 +1; 나는 그것의 단어를 이해하지 못했습니다.
Alex A.

2
아, 내 ski.aditsu.net :) 골프
aditsu

당신은 둘 것을 아마 명시해야 sterm하고 lterm괄호가 누락 된 경우 사용-연관성을 왼쪽으로.
피터 테일러

@PeterTaylor 더 좋은 방법입니까?
FUZxxl

아니요, 실제로 잘못되었다고 생각합니다. 변경된 문법에 SKI따라로 구문 분석 합니다 S(KI).
피터 테일러

답변:


3

하스켈 , 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]

온라인으로 사용해보십시오!

작동 원리

이에 다른 파서 프론트 엔드이다 "유형이 지정되지 않은 람다 계산법에 대한 통역을 쓰기"에 대한 내 대답 도 있고, ungolfed 버전 문서와 있습니다.

간단히 말해서, 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}

원시 문자열에서 β- 환원을 수행하기 위해 정규식 대체를 사용하는 것은 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 초가 걸리는 디버그 출력으로 처리 합니다. α 변환이 작동하는 것을 볼 수 있습니다!

$ 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 : in`gsub ':'I '예제와 함께 잘못된 인수 유형 nil (예정된 Regexp) (TypeError)
aditsu

지금 수정해야합니다! 이미 로컬에서 수정했지만 게시물 수정을 잊었습니다.
Lynn

2
좋아요 ......... l ....................... o ........... w, 그러나 그것은 효과가있는 것 같습니다 .... 결국 :) 나는 S (K (SI)) K의 결과가 정확하지 않다고 생각합니다.
aditsu

9

파이썬 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 의 구분 문자 : 결합기
E = 구문 분석

예 :

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

(이 ↑ SII(SII)는 돌이킬 수 없기 때문에 예상됩니다 )

많은 바이트를 죽이는 데 도움을 준 Mauris와 Sp3000에게 감사드립니다. :)


1
나는 확신 당신이 돌 수 있어요 def m(a,b,c):return foo으로 m=lambda a,b,c:foo당신에게 바이트를 많이 절약 할 수 있습니다, 심지어 내부 클래스.
Lynn

@Mauris 팁 주셔서 감사합니다 :)
aditsu

a.b.c.a(c)(b(c))유효한 람다 식 으로 읽지 못했습니다 (c).
코어 덤프

@coredump 불필요 한 그룹 화가있는 피연산자입니다 ... 그렇습니다 .OP의 문법 규칙과 정확히 일치하지 않습니다. 그것이 얼마나 중요한지 궁금합니다. 내가 물어볼 게.
aditsu

@coredump 이제 업데이트 된 문법으로 괜찮을 것입니다.
aditsu

3

공통 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있었지만 기호에 대해 사용자에게 친숙한 이름을 원합니다. 즉 심볼에서 문자로 명명 된 이유 az(질문에서 허용). 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, cd참조되지 않습니다.

공연

위의 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 초가 걸립니다.


깔끔한 솔루션입니다!
FUZxxl

@FUZxxL 감사합니다!
코어 덤프
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.