IO.popenで遊ぶ

IO.popenでRのインタプリタを開いてRubyからRを操作出来ないかなーと思ってちょっと弄ってみました。
ちなみに遊びとかではなくて普通にRubyからRを使いたい場合はRSRubyというライブラリがちゃんとあるのでそちらでどうぞ。


で、とりあえず適当に書いてみたのがこちら。

require 'timeout'

def eval_r(io, message)
  ret = []

  io.puts(message)

  loop do
    begin
      timeout(1) do
        line = io.gets
        unless line == nil then
          ret << line
        else
          return ""
        end
      end
    rescue Timeout::Error
      break
    end
  end

  ret.shift
  return ret.join
end

#init
r = IO.popen("R --vanilla", "r+")
eval_r(r, "")


# main
puts eval_r(r, "a <- c(1,2,3)")

puts eval_r(r, "sum(a)")


# dispose
r.puts "q()"
eval_r(r, "")

r.close

Rと対話するのに一番の問題は、R側の出力を全て受け取って入力待ち状態になった事をどうやって検出するのか、という点です。このサンプルではとりあえずgetsがタイムアウトしたら入力待ち状態という事にしていますが、これだと処理が重いコードを渡した時に確実に駄目ですね…。しかも毎回タイムアウト待ちするから大変遅いです。あとまぁ"a <- c(1,2,3)"を評価してもirbと違って評価結果が表示されないとか他にも問題は色々と。


やっぱりRSRubyと同様に、C拡張を作ってCからRを直接操作するしかないのかなぁ…