mecab-rubyのMeCab::Tagger#parseToNodeの戻り値が気に入らないのでアレする

mecab-rubyでテキストを形態素解析するときによく使うMeCab::Tagger#parseToNodeですが,戻り値がMeCab::NodeのArrayやEnumeratorではなく,以下のようにMeCab::Node#nextを使って各要素にアクセスしなければならないので使い勝手が悪いです.

# -*- coding: utf-8 -*-

require "MeCab"

if __FILE__ == $0
  node = MeCab::Tagger.new.parseToNode("俺、この戦争が終わったら結婚するんだ。")

  while node
    puts "#{node.surface}\t#{node.feature}"
    node = node.next
  end
end

while nodeとかnode = node.nextとか,非常にRubyらしくない感じでアレですね.eachとか使いたい.(メソッド名がCamelCaseなのも非常にアレですが,mecab-rubyはSWIGでコードを自動生成しているみたいなのである程度は仕方ないのかも)

幸いなことにRubyは既存のクラスを自由に書き換えられるので(オープンクラス),以下のようにparseToNodeを再定義してNodeのArrayを返すようにすればこの問題は解決します.

# -*- coding: utf-8 -*-

require "MeCab"

module MeCab
  class Tagger
    # parseToNodeの別名(エイリアス)を作ってprivate化する
    alias_method :parseToNode_org, :parseToNode
    private :parseToNode_org

    # parseToNodeを再定義する
    def parseToNode(*args)
      #オリジナルのparseToNodeを呼び出す
      node = parseToNode_org(*args)
      # NodeのArrayを作って返す
      nodes = []
      while node
        nodes.push(node)
        node = node.next
      end
      nodes
    end
  end
end

if __FILE__ == $0
  nodes = MeCab::Tagger.new.parseToNode("俺、この戦争が終わったら結婚するんだ。")

  # Array#eachが使える!
  nodes.each do |node|
    puts "#{node.surface}\t#{node.feature}"
  end
end

オープンクラスって素敵.

上のRubyスクリプトをMeCabの代わりにrequireするだけでNodeのArrayを返すparseToNodeが使えるようになるので,while nodeとnode = node.nextに嫌気が差したら是非試してみてください.