优化SKI编译器


22

SKI演算是不使用Lambda表达式lambda演算的变体。相反,只有应用程序和组合器SK,和使用。在此挑战中,您的任务是将SKI术语转换为β范式的 Lambda术语。


输入规格

输入是以下文本表示形式的SKI术语。您可以选择接收可选的尾随换行符。输入是由字符SKI(,和)与满足以下语法(在ABNF形式)sterm作为起始符号:

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

输出规格

输出是以下文本表示中没有自由变量的lambda术语。您可以选择输出可选的尾随换行符。输出应满足以下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

计分

以八位字节为单位的最短解决方案将获胜。禁止常见漏洞。


7
+1是因为我认为这是一个很酷的挑战;我什么都不懂。
Alex A.

2
啊,我应该打我的ski.aditsu.net :)
aditsu

您可能应该声明两者都没有,stermlterm在缺少括号时使用左联想。
彼得·泰勒

@PeterTaylor这样更好吗?
FUZxxl

不,我认为这实际上是错误的:在语法改变后,我将解析SKIS(KI)
彼得·泰勒

Answers:


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]

在线尝试!

怎么运行的

这是我回答“为无类型的lambda演算写一个解释器”的答案的另一种解析器前端,它也有一个非高尔夫版本带有文档的。

简而言之,Term = T (Char -> String)是lambda演算术语的类型,它们知道如何将自己应用到其他术语(a :: Term -> Term -> Term)以及如何将自身显示为String(%) :: Term -> Char -> String),并给定初始新鲜变量为a Char。我们可以使用来将术语的函数转换为术语l :: (Term -> Term) -> Term,并且由于生成的术语的应用仅调用函数(a (l f) == f),因此术语在显示时会自动还原为普通形式。


9

Ruby,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':错误的参数类型nil(预期的Regexp)(TypeError),带有“ I”示例
aditsu

现在应该修复!我已经在本地更正了它,但是忘了编辑我的帖子。
林恩

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/背后的代码,翻译为python,经过了大大简化和精打细算。

参考:(由于代码已压缩,这可能不太有用)

V =变量项
A =应用程序项
L = lambda项
c =变量计数器
p =用项
r 替换变量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,这可以为您节省很多字节。
林恩

@毛里斯感谢小费:)
aditsu

我无法读取a.b.c.a(c)(b(c))为有效的lambda表达式:什么是(c)
coredump

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

减少Beta

使用,可以在归约过程中将变量动态绑定PROGV到新的Common Lisp符号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))
  • 编辑我更新了我的答案,以便与atsutsu的答案具有相同的分组行为,因为它花费的字节数更少。
  • 其余的差异可以看出对于测试6和8的结果a.a.a.a.a.b.a是正确的,并尽可能多的字母Python的答案,在哪里绑定不使用abcd没有被引用。

性能

立即遍历以上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 2015年

@FUZxxl谢谢!
coredump
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.