c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

RubyでFactorっぽい連鎖性 concatenative (関数合成) 記法を作る

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)