3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

mecab-rubyのMeCab::Tagger#parseToNodeをEnumberatorする

Last updated at Posted at 2015-01-03

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で、「単方向線形連結リスト」から、「単方向イテレータ」になる。配列を構造するのを避けた。

3
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?