ちょー簡略版unlambda

このところSKIコンビネータで遊んでいたのですが、そういえばSKIコンビネータをベースとしたunlambdaって関数型言語があったなぁ思い出しました。ということでちょー簡略版のunlambdaを実装してみました。組込み関数は.x、r、i、k、sのみ実装しています。

require 'stringio'

def nlambda(n = -1, xs = [], &f)
  n = f.arity if n < 0
  if n == 0
    f[*xs]
  else
    lambda {|x| nlambda(n - 1, xs + [x], &f) }
  end
end

FUNC_TBL = Hash.new {|h, c| c.chr.to_sym }
FUNC_TBL[?.] = nlambda {|x, y   | putc x; y  }
FUNC_TBL[?r] = nlambda {|x      | puts; x    }
FUNC_TBL[?i] = nlambda {|x      | x          }
FUNC_TBL[?k] = nlambda {|x, y   | x          }
FUNC_TBL[?s] = nlambda {|x, y, z| x[z][y[z]] }

def evaluate(src)
  src = StringIO.new(src.to_s) unless src.respond_to?(:getc)
  c = src.getc
  case c
  when ?` then evaluate(src)[evaluate(src)]
  when ?. then FUNC_TBL[c][src.getc]
  else         FUNC_TBL[c]
  end
end

# Hello, world!プログラム
p evaluate('`r`````````````.H.e.l.l.o.,. .w.o.r.l.d.!.a')
# => Hello, world!
#    #<Proc:0x02b37570@unlambda.rb:11>

# SKK = Iの例
p evaluate('```skkA')
# => :A

p evaluate('`iA')
# => :A

p evaluate('```skkA') == evaluate('`iA')
# => true

# フィボナッチ数の数だけ「*」を表示するプログラム
p evaluate('```s``s``sii`ki`k.*``s``s`ks``s`k`s`ks``s``s`ks``s`k`s`kr``s`k`sikk`k``s`ksk')
# => 
#    *
#    *
#    **
#    ***
#    *****
#    ********
#    *************
#    *********************
#    **********************************
#    *******************************************************
#    ...

おお。意外と短かく綺麗にまとまりました。しかしオリジナルのunlambdaは遅延評価とかcallccの組込み関数もあるのか。やるなー。
参照: http://hw001.gate01.com/eggplant/tcf/unlambda/