第6回北海道開発オフに参加してきた!

謹賀新年。昨年のうちに書けなかったことを、休みのあいだに書いてしまおうというわけです。
今回もモクモクと書いてきました。日本語の動詞を活用するためのクラスの叩き台です。

背景

今回は@niku_name氏が取り組んでましたが、日本語を分析する手法として、私たちはすでに、形態素解析という方法を手にしています。形態素解析では、以下のように、文を単語に分割することができます。

もとの文:「このように、形態素に分割することができます。」

この    この    コノ            連体詞
よう    よう    ヨウ            名詞-非自立-助動詞語幹
に      に      ニ              助詞-副詞化
、      、      、              記号-読点
形態素  形態素  ケイタイソ      名詞-一般
に      に      ニ              助詞-格助詞-一般
分割    分割    ブンカツ        名詞-サ変接続
する    する    スル            動詞-自立       サ変・スル      基本形
こと    こと    コト            名詞-非自立-一般
が      が      ガ              助詞-格助詞-一般
でき    できる  デキ            動詞-自立       一段    連用形
ます    ます    マス            助動詞  特殊・マス      基本形
。      。      。              記号-句点

しかし、その分割した結果を自由自在に再構成することは、今のところ困難です。私たちは、「できません」という表現を「でき」+「ませ」+「ん」に分割し「できる」「ます」「ん」という原形を得ることはできますが、「できる」「ます」「ん」の組をくっつけようとしても、そのままだと「できるますん」になってしまいます。正しくつなげるためには、操作の過程で、「できる」を「でき」に、「ます」を「ませ」に活用する必要があります。
よくあるマルコフ連鎖を使っているボットなどでは、活用する品詞である動詞などについて、その活用した結果のみ(「歩か」や「見れ」など)をデータとして持っているために、その問題は解消されています。ただし、この手法では、「丁寧な表現」や「否定の表現」といった意味表現についての操作の自由度がなくなってしまいます。「できる」という動詞に「否定」「丁寧」という意味を加えて「できません」を得る、というような操作をプログラム上で実現するには、この問題は解決されなくてはいけません。
そんなわけで、今回は動詞の活用をするためのクラスをRubyで書いてみました。ただし、CaboCha::Token型の品詞情報が得られていることが前提。動詞について、文字列としての情報(「歩く」「見る」など)と、活用型の情報(五段・一段・カ変・サ変)がわかっていれば、あとに続く助動詞に合わせて的確に活用できるというものです。

verb.rb

#!/usr/bin/ruby

class Verb
  def initialize(token)
    if token.pos !~ /^動詞/ then
      @stem = ''
      @conj = ''
    end
    if token.ctype[/^五段/] then
      @stem = token.base.split(//u)[0..-2].join 
      @conj = Conj.new('godan', token.base.split(//u)[-1])
    elsif token.ctype[/^一段/] then
      @stem = token.base.split(//u)[0..-2].join 
      @conj = Conj.new('ichidan')
    elsif token.ctype[/^サ変/] then
      if token.base[/する$/] then
        @stem = token.base.sub(/する$/, '')
        @conj = Conj.new('sahen', 'する')
      elsif token.base[/ずる$/] then
        @stem = token.base.sub(/ずる$/)
        @conj = Conj.new('sahen', 'ずる')
      else
        @stem = ''
        @conj = ''
      end
    elsif token.ctype[/^カ変/] then
      if token.base[/来る$/] then
        @stem = token.base.sub(/来る$/, '')
        @conj = Conj.new('kahen', '来る')
      elsif token.baes[/くる$/] then
        @stem = token.base.sub(/くる$/, '')
        @conj = Conj.new('kahen', 'くる')
      else
        @stem = ''
        @conj = ''
      end
    else
      @stem = ''
      @conj = ''
    end
  end
  def auxil_conjugate(token)
    if token.pos !~ /^助動詞/ then
      @stem + @conj.shushi
    end
    if ['ない', '', ''].include?(token.base) then
      @stem + @conj.mizen
    elsif ['たい', 'ます'].include?(token.base) then
      @stem + @conj.renyou
    elsif ['まい', 'らしい'].include?(token.base) then
      @stem + @conj.shushi
    elsif token.base == '' then
      @stem + @conj.mizen_u
    elsif token.base == '' then
      @stem + @conj.renyou_ta
      # このままだと連濁「だ」ができない
    else
      @stem + @conj.shushi
    end
  end
end

class Verb::Conj
  Godan =
    [
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
     ['', '', '', '', '', '', ''],
    ]
  Ichidan = ['', '', '', '', ['', ''], '', '']
  Sahen_s = ['する', ['', '', ''], ['', '', ''], 'すれ', ['しろ', 'せよ'], '', '']
  Sahen_z = ['ずる', ['', '', ''], ['', '', ''], 'ずれ', ['じろ', 'ぜよ'], '', '']
  Kahen_h = ['くる', '', '', 'くれ', 'こい', '', '']
  Kahen_k = ['来る', '', '', '来れ', '来い', '', '']
  def initialize(type, cons = nil)
    @type = type
    @cons = if @type == 'godan' then
      Godan.assoc(cons)
    elsif @type == 'ichidan' then
      Ichidan
    elsif @type == 'sahen' then
      if cons == 'する' then
        Sahen_s
      else
        Sahen_z
      end
    elsif @type == 'kahen' then
      if cons == 'くる' then
        Kahen_h
      else
        Kahen_k
      end
    end
  end
  def shushi
    @cons[0]
  end
  def mizen_u
    @cons[1]
  end
  def mizen
    @cons[2]
  end
  def katei
    @cons[3]
  end
  def meirei
    @cons[4]
  end
  def renyou_ta
    @cons[5]
  end
  def renyou
    @cons[6]
  end
end

サ変以外はほぼ活用できる予感。まだ音便の処理は組み込んでいません。ケーススタディが不足していますが、思うにAPIに問題があるのかなと。助動詞などを引数として与えるより、「過去」とか「推量」とかの意味素性をメソッドに割り当てるほうが現実的かもしれませんが、接続のパターンが多様すぎて、どうやってすべてに対応したものか、大変悩ましいです。