Rubyでcurry化しない関数合成のcompose関数をみてゐたら、Factorに見えてきて、作れさうに思ふた。
再帰で階乗を定義できる所迄作る。
# coding=utf-8 # license: Public Domain class Factor def initialize *words @definitions = {} @stack = [] call *words end def call *words while words.length > 0 word = words.shift if word.class.method_defined? :call arity = word.arity result = if arity < 0 word.call *(@stack.pop(-arity) + [self]) elsif arity == 0 word.call else word.call *(@stack.pop(arity - 1) + [self]) end next if result == nil if result.is_a? Array @stack += result else @stack << result end elsif word.is_a?(Symbol) && @definitions.key?(word) @stack << @definitions[word] else @stack << word end end self end def define name, word; @definitions[name] = word; end def undef name; @definitions.delete name; end def local names, words names.reverse.map{|name| ->(v, ctx){ ctx.define(name, v); nil } } + words + names.map{|name| ->(ctx){ ctx.undef(name); nil } } end end
変数名が衝突するのでlocal scopeとは云へないが、一時的な変数を定義できる所迄は作った。以下の樣に使ふ。
Factor.new *words Factor.new.call *words
以下の基礎的なwordを定義しておく。
dup = ->(v, ctx){ [v, v] } swap = ->(u, v, ctx){ [v, u] } drop = ->(*v){} call = ->(words, ctx){ ctx.call(*words); nil } times = ->(n, words, ctx){ n.times{ ctx.call *words }; nil } if_ = ->(bool, words1, words2, ctx) do if bool ctx.call *words1 else ctx.call *words2 end nil end print = ->(v, ctx){ Kernel.print v } append = ->(str1, str2, ctx){ str1 + str2 } to_s = ->(v, ctx){ v.to_s } sub = ->(n, m, ctx){ n - m } prod = ->(n, m, ctx){ n * m } is_eq = ->(u, v, ctx){ u == v } is_lt = ->(u, v, ctx){ u < v }
条件分岐は此う。
# true [ then_words ] [ else_words ] if Factor.new true, [*then_words], [*else_words], if_
単純な繰り返しは此う。
# 10 [ words ] times Factor.new 10, [*words], times
一時変数は以下の樣に作る。xとyと云ふ変数を作り、積を求める。再代入はできない。
# [| x y | x y * ] call Factor.new ->(ctx){ ctx.local [:x, :y], [:x, :y, prod] }, call
Factorの公式Web siteのtopに有るcode例は、此う。
Factor.new. call("Hello world", print). call(10, [ "Hello, Factor", print ], times). call("Hello, ", "Factor", append, print)
Factor で階乗。然して再帰に就いて抄で作った階乗は、此う。
# Factor で階乗。然して再帰に就いて抄 http://c4se.hatenablog.com/entry/2013/12/30/030205 factorial_rec = -> ctx do # n, n -- n ctx.call dup, 0, is_eq, [drop], [ ctx.local([:n, :m], [:n, :m, prod, :m, 1, sub, factorial_rec]), call ], if_ nil end factorial = -> ctx do # n -- n ctx.call dup, 0, is_lt, [drop, 0], [1, swap, factorial_rec], if_ nil end Factor.new(10, factorial, to_s, "\n", append, print)