SKI derleyicisini optimize etme


22

SKI taşı lambda ifadeleri kullanmaz lambda hesap bir varyantıdır. Bunun yerine, yalnızca uygulama ve birleştiriciler S , K ve ben kullanılır. Bu zorlu görevde, göreviniz SKI terimlerini Lambda terimlerine normal biçimde çevirmektir .


Giriş özellikleri

Giriş, aşağıdaki metin gösterimlerinde bir SKI terimidir. İsteğe bağlı bir izleyen yeni hat almayı seçebilirsiniz. Giriş karakterler içerir S, K, I, (ve )ile tatmin (ABNF formunda) şu gramer stermbaşlangıç simgesi söz konusu olur:

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

Çıkış özellikleri

Çıktı, aşağıdaki metin gösterimlerinde serbest değişkenleri olmayan bir lambda terimidir. İsteğe bağlı bir izleyen yeni satır çıktısını seçebilirsiniz. Çıktı, ABNF formundaki ltermbaşlangıç gramerini içeren aşağıdaki gramerleri karşılar :

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

Kısıtlamalar

Girişin normal formda olduğunu varsayabilirsiniz. Normal formun en fazla 26 farklı değişken kullandığını varsayabilirsiniz. Hem giriş hem de çıkışın 79 karakterle gösterilebileceğini varsayabilirsiniz.

Örnek girişler

İşte birkaç örnek girdi. Çıkış, verilen giriş için olası bir çıkıştır.

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

puanlama

Oktetlerde en kısa çözüm kazanır. Ortak boşluklar yasaktır.


7
+1 çünkü bunun havalı bir meydan okuma olduğunu düşünüyorum; Bir kelimesini anlamadım.
Alex A.

2
Ah, benim ski.aditsu.net golf oynamak gerekir :)
aditsu

Muhtemelen ikisini de belirtmelisiniz stermve ltermparantezler eksikken soldan ilişkilendirmeli kullanmalısınız.
Peter Taylor

@PeterTaylor Bu şekilde daha iyi?
FUZxxl

Hayır, bunun aslında yanlış olduğunu düşünüyorum: Değiştirilen gramer ı ayrıştırmak olacağını aşağıdaki SKIşekilde S(KI).
Peter Taylor,

Yanıtlar:


3

Haskell , 232 bayt

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]

Çevrimiçi deneyin!

Nasıl çalışır

Bu, farklı bir ayrıştırıcıdır ve belgelere sahip, asılsız bir versiyona sahip olan Yazılmamış lambda matematiği için bir tercüman yazın” cevabım .

Kısaca, Term = T (Char -> String)kendilerini diğer terimlere nasıl uygulayacaklarını ( a :: Term -> Term -> Term) ve kendilerini nasıl bir String( (%) :: Term -> Char -> String) olarak göstereceklerini , bir ilk taze değişken olarak verdikleri bilinen lambda hesabı türüdür Char. Terimlerdeki bir işlevi bir terime dönüştürebiliriz l :: (Term -> Term) -> Termve elde edilen terimin uygulanması yalnızca işlevi ( a (l f) == f) çağırdığı için , terimler görüntülendiğinde otomatik olarak normal forma indirgenir.


9

Ruby, 323 bayt

Bu saçmalıkların işe yarayacağına inanamıyorum:

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}

Ham tellerde β azaltma gerçekleştirmek için regex ornatımı kullanmak bazı Tony-the-Pony şeylerdir. Bununla birlikte, çıktısı en azından kolay test senaryoları için doğru görünüyor:

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

İşte K(K(K(KK)))benim dizüstü bilgisayarda yaklaşık 7 saniye süren bazı hata ayıklama çıkışı ile ele alıyor, çünkü düzenli ifade özyineleme yavaş . Α dönüşümünü çalışırken görebilirsiniz!

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

Ben alıyorum: ski.rb: 4: `gsub içinde: I 'Örneğin' (Normal İfade beklenen) yanlış argüman tipi nil (TypeError) ile'
aditsu

Şimdi düzeltilmeli! Yerel olarak zaten düzeltmiştim, ancak yayınımı düzenlemeyi unuttum.
Lynn,

2
Tamam, ................................... ama iş gibi görünüyor .... sonunda :) Ben S (K (SI)) K için sonuç olsa doğru olmadığını düşünüyorum.
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)'})

Not: sonra while 1:, 3 satır bir sekme karakteriyle girintilidir.

Bu temelde, http://ski.aditsu.net/ adresinin python'a çevrilmiş, büyük ölçüde basitleştirilmiş ve ağır bir şekilde golf oynamasının arkasındaki koddur.

Referans: (bu, kodun sıkıştırılmış olması durumunda muhtemelen daha az kullanışlı)

V = değişken terimi
A = uygulama terimi
L = lambda terimi
c = değişken sayacı
p = değişkeni
r = yerine değişkenle değiştir =
m = son değişken yeniden numaralandırma
u = dahili değişken yeniden numaralandırma (kopyalanan terimler için)
s = string dönüşüm
(parametre s = öz)
C = string dönüşüm için ayırıcı karakter (ler)
I, K, S: birleştiriciler
E = ayrıştırma

Örnekler:

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

(Bu ↑ bekleniyor çünkü SII(SII)indirgenemez)

Bir sürü baytı öldürmek için Mauris ve Sp3000'e teşekkür ederiz :)


1
Oldukça emin açabilirsiniz olduğum def m(a,b,c):return fooiçine m=lambda a,b,c:foosize bayt sürü kurtarabilir hatta iç sınıflar.
Lynn,

@Mauris bahşiş için teşekkür ederim :)
aditsu

a.b.c.a(c)(b(c))Geçerli bir lambda ifadesi olarak okuyamıyorum : nedir (c)?
coredump

@ coredump gereksiz gruplandırmaya sahip bir operand ... ve haklısınız, OP'nin gramer kurallarına tam olarak uymuyor. Bunun ne kadar önemli olduğunu merak ediyorum; Soracağım.
aditsu

@ coredump Güncellenmiş dilbilgisi ile şimdi tamam olmalı.
aditsu, 21:15

3

Ortak Lisp, 560 bayt

"Sonunda bir kullanım buldum 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-azaltma

Değişkenler, azaltma sırasında PROGVyeni Common Lisp sembollerine göre dinamik olarak bağlanır MAKE-SYMBOL. Bu çarpışmaların isimlendirilmesinden hoş bir şekilde kaçınmayı sağlar (örneğin sınır değişkenlerinin istenmeyen gölgelenmesi). Kullanabilirdim GENSYM, ancak semboller için kullanıcı dostu isimler kullanmak istiyoruz. Yani semboller gelen harflerle adlandırılır yüzden ahiç z(Söz izin verdiği şekilde). Nmevcut kapsamdaki bir sonraki harfin karakter kodunu gösterir ve 97, aka ile başlar a.

İşte R( Wmakro olmadan ) daha okunaklı bir sürümü :

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

Orta sonuç

Dizeden ayrıştır:

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

azaltın:

CL-USER> (r *)
(L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|a| (L #:|b| #:|a|))))))

(İcra izine bakınız)

Pretty-print:

CL-USER> (o *)
"a.a.a.a.a.b.a"

Testler

Python cevabıyla aynı test takımını tekrar kullanıyorum:

        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. test örneği, yukarıdaki tablo için çok büyük:

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))
  • DÜZENLEME ile aynı gruplama davranışına sahip olmak için cevabımı güncelledim. Aditsu'nun cevabında , çünkü yazmak daha az byte'a mal oldu.
  • Kalan fark sonucu testler 6 ve 8 için görülebilir a.a.a.a.a.b.adoğru olduğunu ve bağlamaları için Python cevap olarak çok harflerle gibi kullanmaz a, b, cve dbelirtilmemiştir.

performans

Yukarıdaki 7 geçiş testinin üzerinden geçerek sonuçları toplamak hemen gerçekleşir (SBCL çıkışı):

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

Aynı testi yüzlerce kez yapmak ... Özel değişkenlerle ilgili bilinen bir sınırlama nedeniyle, SBCL'de "Lokal yerel depolama bitmiştir" sonucunu veriyor . CCL ile aynı test takımının 10000 kez aranması 3.33 saniye sürer.


Bu temiz bir çözüm!
FUZxxl,

@ FUZxxl Teşekkürler!
coredump
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.