LoginSignup
0
1

More than 5 years have passed since last update.

Perl 6でNLP 5本ノック 『第4章: 形態素解析』 30~34

Last updated at Posted at 2016-12-13

こんにちは、Perl 6アドベントカレンダーの14日目の投稿になります。

NLP 5本ノックということで、東北大学の言語処理100本ノックの『第4章: 形態素解析』の 30~34までの5本をPerl 6で解きつつ解説をしていきたいと思います。

問題文は掲載しませんので、ブラウザの別窓で下記ページを参照しながらお楽しみください:
http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch4

読み進める前に

注意!

  • 読者レベルとしては、Perl 6アドベントカレンダー1日目で紹介したPerl 6 introductionなどのチュートリアルを一通り終えたレベルを想定しています。
  • わかりやすく紹介するために、正確ではない表現がところどころ出てくるかもしれません。
  • 日本語訳が定着していないようなPerl 6独特の英単語を目にするかもしれません。基本的に解説中ではそのまま英単語で書き、その後補足しますのでご容赦ください。
  • Perl 6のモットーはTMTOWTDI (やり方は一つじゃない) です。特に正解はありません。でも、「これのほうがもっとかっこよく簡潔に書けるよ!」といった指摘は大歓迎です!
  • 元の30~34の問題文だと、30で生成されたデータをそれ以降で使う流れになっています。しかし、ここではバインダの使い方を紹介したかった関係で無視しているので注意してください。

準備

  • 拙作のMeCabのPerl 6バインダを入れてください。下記コマンドで、READMEに書いてあるようにIPAdicも勝手に入ります。
$ zef install MeCab

30. 形態素解析結果の読み込み

コード

30.p6
use MeCab;
use MeCab::Tagger;

my $fh = open "neko.txt", :r;
my MeCab::Tagger $tagger .= new;
my @list;
for $fh.lines {
    loop (my MeCab::Node $node = $tagger.parse-tonode($_); $node; $node = $node.next) {
        my @tmp = $node.feature.split(",");       
        @list.push({:surface($node.surface), :base(@tmp[6]),
                    :pos(@tmp[0]), :pos1(@tmp[1])});
    }
    .say for @list;
}
$fh.close;
  1. open "neko.txt", :r で読み取り専用モードでファイルを開き、ファイルハンドルを変数に代入します。
  2. 一行ずつ読み取っていきます
    1. $tagger.parse-tonode で与えられた行を形態素解析します
    2. $node.surfaceはsurfaceに、$node.featureの7,0,1列目はこの順でbase,pos,pos1の値に代入し、ハッシュを作ります
    3. 2.のハッシュを@listにpushします
  3. .say for @list;で出力します

31. 動詞

31.p6
use MeCab;
use MeCab::Tagger;

my $fh = open "neko.txt", :r;
my MeCab::Tagger $tagger .= new;
for $fh.lines {
    loop (my MeCab::Node $node = $tagger.parse-tonode($_); $node; $node = $node.next) {
        $node.surface.say if $node.posid == 31|32|33;
    }
}
$fh.close;

解説

  1. open "neko.txt", :r で読み取り専用モードでファイルを開き、ファイルハンドルを変数に代入します。
  2. 一行ずつ読み取っていきます
    1. $tagger.parse-tonode で与えられた行を形態素解析します
    2. 形態素のposidが31|32|33だった場合に$node.surface.sayで表記を出力します

補足

  • posidとは品詞IDのことです。下記コマンドでチェックしてみましょう:
$ nkf -w ~/.p6mecab/lib/mecab/dic/ipadic/pos-id.def | less

下記のような出力が得られたはずです。見ての通り31~33は動詞です。:

...
接頭詞,動詞接続,*,* 29
接頭詞,名詞接続,*,* 30
動詞,自立,*,* 31
動詞,接尾,*,* 32
動詞,非自立,*,* 33
副詞,一般,*,* 34
副詞,助詞類接続,*,* 35
名詞,サ変接続,*,* 36
...

32. 動詞の原形

コード

use MeCab;
use MeCab::Tagger;

my $fh = open "neko.txt", :r;
my MeCab::Tagger $tagger .= new;
for $fh.lines {
    loop (my MeCab::Node $node = $tagger.parse-tonode($_); $node; $node = $node.next) {
        $node.feature.split(",",:skip-empty).[6].say if $node.posid == 31|32|33;
    }
}
$fh.close;

解説

  1. open "neko.txt", :r で読み取り専用モードでファイルを開き、ファイルハンドルを変数に代入します。
  2. 一行ずつ読み取っていきます
    1. $tagger.parse-tonode で与えられた行を形態素解析します
    2. 形態素のposidが31|32|33だった場合に$node.feature.split(",",:skip-empty).[6].sayでコンマ区切りで、featureの7列目を出力します

33. サ変名詞

コード

use MeCab;
use MeCab::Tagger;

my $fh = open "neko.txt", :r;
my MeCab::Tagger $tagger .= new;
for $fh.lines {
    loop (my MeCab::Node $node = $tagger.parse-tonode($_); $node; $node = $node.next) {
        $node.surface.say if $node.posid == 36;
    }
}
$fh.close;

解説

  • "31. 動詞" の品詞IDがサ変接続の36に変わっただけです。

34. 「AのB」

コード

use MeCab;
use MeCab::Tagger;

my $fh = open "neko.txt", :r;
my MeCab::Tagger $tagger .= new;
for $fh.lines {
    my @surfaces = gather loop (my MeCab::Node $node = $tagger.parse-tonode($_); $node; $node = $node.next) {
        if $node.next.defined and $node.next.next.defined {
            take $node.surface ~ 'の' ~ $node.next.next.surface if $node.posid R(cont) 36..67 and $node.next.surface eq 'の' and $node.next.next.posid R(cont) 36..67;
        }
    }
    .say for @surfaces;
}
$fh.close;

解説

  1. open "neko.txt", :r で読み取り専用モードでファイルを開き、ファイルハンドルを変数に代入します。
  2. 一行ずつ読み取っていきます
    1. $tagger.parse-tonode で与えられた行を形態素解析します
    2. 形態素のならびを3つごとに見ていきます。この3つの並びの形態素の先頭をA、真ん中をB、末尾をCと置くと、「Aのposid`が36~67のどれか」かつ「Bの表記が"の"」かつ「Cのposidが36~67のどれか」のときに、その3つの形態素の並びを取り出します。

補足

if $node.next.defined and $node.next.next.defined

  • ifじゃなくてwithかなあと若干思ったんですが、roastにそういうケースがなかったので回避しています。

R(cont)

  • $a (cont) $b(cont)と等価です。 $a$bを元として含むを意味します。
  • Rはreversed operator(逆転演算子)と呼ばれるものです。被演算子を逆転させます。つまり$a R(cont) $bは、$b$aを元として含むを意味します。論文とかだとこっち向きが多い[要出典]と思うのでRをつけています。

以上、Perl 6でNLP 5本ノック『第4章: 形態素解析』 30~34 でした。

0
1
0

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
0
1