MeCabは伝統的なライブラリーなので、インタフェースの中にEnumberatorという概念がない。mecab-rubyはSWIGで実装されるものなので、MeCabのC++のAPIをそのままにrubyに接続することである。だからMeCab::Tagger#parseToNodeの戻り値もEnumberatorではなく、C++風のMeCab::Nodeである。
# -*- coding: utf-8 -*-
require "mecab"
node = MeCab::Tagger.new.parseToNode("すもももももももものうち")
while node
puts "#{node.surface}\t#{node.feature}"
node = node.next
end
Enumberatorの戻り値が欲しいから、調べると、mickey24さんの『mecab-rubyのMeCab::Tagger#parseToNodeの戻り値が気に入らないのでアレする』という文を拝読した。mickey24さんの方法は下記である。
# -*- 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::Nodeは「単方向線形連結リストのノード」であり、NodeのArrayを作って返してしまうと、少しメモリを無駄にする。 -
再定義する影響が大きい。
Ruby特有なやり方で、MeCab::TaggerをOpenしてparseToNodeを再定義するのは、慎重にしなければならないことである。再定義したら、古い呼び出し方が無効になると、特に大きいプロジェクトの中に、影響が大きすぎると思う。柔軟に再定義したほうかいいと思う。
上記の理由で、下記の再定義やり方を考えた:
# -*- coding: utf-8 -*-
require "mecab"
module MeCab
class Tagger
# parseToNodeの別名(エイリアス)を作ってprivate化する
alias_method :parseToNode_org, :parseToNode
private :parseToNode_org
def parseToNode(*args)
#オリジナルのparseToNodeを呼び出す
node = parseToNode_org(*args)
return node unless block_given?
# NodeのEnumberatorを作って返す
while node
yield node
node = node.next
end
end
end
end
if __FILE__ == $0
# Ruby風に使える
MeCab::Tagger.new.parseToNode("俺、この戦争が終わったら結婚するんだ。") {|node|
puts "#{node.surface}\t#{node.feature}"
}
# 古い使い方も出来る
node = MeCab::Tagger.new.parseToNode("すもももももももものうち")
while node
puts "#{node.surface}\t#{node.feature}"
node = node.next
end
end
要するに、
-
互換性を得る:
block_given?での判断で、古い呼び出し方も出来る。 -
メモリ安定:
yield nodeで、「単方向線形連結リスト」から、「単方向イテレータ」になる。配列を構造するのを避けた。