メソッドの動的呼び出し

自分用メモ。

単語のポジネガをぱっと見で確認するのに結果を色付きで見れると便利。Rubyでそういうgemを作っている人がいたので使わせてもらう。

require "colored"
puts "this is red".red

とかやるとターミナルに赤く表示される。が、色々色があるときにcaseで分けてやるとか面倒なので、"red"とかのmethodを呼び出すのを動的にしたい。結論からいうとKernel#sendを使うとうまく扱うことができる(すぐに忘れる)。

実際の例。例えばこんなファイルから色を付けたい。

% cat data/WordLevel_10.txt
Document-Level Polarity: 0.512755, 0.487245
0.833333, 0, 0.166667: ,(0.5, 0, 0.5) sorry(1, 0, 0) bill(1, 0, 0) 
0.423077, 0.461538, 0.115385: this(0.5, 0.5, 0) and(0.5, 0, 0.5) have(0.5, 0.5, 0) a(0, 0.5, 0.5) only(1, 0, 0) made(0, 1, 0) most(0.5, 0.5, 0) purchase(0, 0.5, 0.5) poor(0.5, 0.5, 0) fair(0.5, 0.5, 0) production(0.5, 0.5, 0) disappointing(0, 1, 0) lyrics(1, 0, 0) 
0.346154, 0.423077, 0.230769: this(0.5, 0.5, 0) to(0, 0.5, 0.5) the(0, 1, 0) is(0.5, 0.5, 0) ,(0.5, 0.5, 0) but(0.5, 0, 0.5) enough(0.5, 0, 0.5) material(0, 0.5, 0.5) save(0, 1, 0) adequate(0, 0.5, 0.5) playing(1, 0, 0) guitar(0.5, 0.5, 0) not_good(0.5, 0, 0.5) 
0.111111, 0.666667, 0.222222: the(0, 0.5, 0.5) i(0, 1, 0) a(0, 0.5, 0.5) thought(0.5, 0.5, 0) on(0, 0, 1) more(0.5, 0.5, 0) out(0, 1, 0) little(0, 0, 1) time(0, 1, 0) before(0, 1, 0) material(0, 1, 0) his(0, 1, 0) bill(0.5, 0, 0.5) next(0, 1, 0) hope(0, 1, 0) process(0, 1, 0) bringing(0.5, 0, 0.5) spends(0, 1, 0) 

で、こんなスクリプトをでっちあげる。

require "colored"

# RubyにはRでいうところのwhich.minみたいな関数はないんでしたっけ...?
class Array
  def max_index
    max_index = 0
    max = - 1.0/0
    self.each_with_index{|elem, index|
      if elem > max
        max = elem
        max_index = index 
      end
    }
    return max_index
  end
end

polarity2color = Hash.new
polarity2color[0] = "blue"
polarity2color[1] = "red"
polarity2color[2] = "green"

if ARGV.size != 1
  puts "usage; ruby #{__FILE__} filename"
  exit
end

filename = ARGV[0]

if !File.exist?(filename)
  STDERR.puts "#{filename} doesn't exist!"
  exit 
end

File.open(filename, "r") {|file|
  skip_num = file.gets.split("\s").last.to_i
  skip_num.times do
    file.gets
  end
  file.each{|line|
    line.chomp!
    if line =~ /(\d*.\d*), (\d*.\d+), (\d*.\d+): (.*)/
      sentence_pos_prob = $1; sentence_neg_prob = $2; sentence_neu_prob = $3; rest = $4
      sentence_pos_prob = sentence_pos_prob.to_f; sentence_neg_prob = sentence_neg_prob.to_f; sentence_neu_prob = sentence_neu_prob.to_f
      sentence_prob = [sentence_pos_prob, sentence_neg_prob, sentence_neu_prob]
      max_index = sentence_prob.max_index
      print "#{sentence_prob[max_index].to_s.send(polarity2color[max_index]).send(:underline)} "
      rest.scan(/(.*?)\((\d*.\d*), (\d*.\d*), (\d*.\d*)\) /).each{|chunk|
        word, word_pos_prob, word_neg_prob, word_neu_prob = chunk
        word_pos_prob = word_pos_prob.to_f; word_neg_prob = word_neg_prob.to_f; word_neu_prob = word_neu_prob.to_f
        word_prob = [word_pos_prob, word_neg_prob, word_neu_prob]
        print "#{word.send(polarity2color[word_prob.max_index])} "
      }
      puts 
    end
  }
}

すると、こんな感じの出力を得ることができる。ターミナルの上で見れるので(自分にとって)便利。
~/ntt_intern/cpp/SentenceLevel ~/ntt_intern/cpp/SentenceLevel/data ~/ntt_intern/cpp/SentenceLevel ~/ntt_intern/cpp/Se — ssh — 117×84

sendとかの分かりやすい例はRubyベストプラクティス -プロフェッショナルによるコードとテクニックのp75とかに載っていた。