2018年04月02日

言語処理100本ノックでPython入門 #31 - yield fromが便利すぎる

  
言語処理100本ノック 2015の問題31を解きました。

■ 問題

夏目漱石の小説『吾輩は猫である』の文章(neko.txt)をMeCabを使って形態素解析し,その結果をneko.txt.mecabというファイルに保存せよ.このファイルを用いて,以下の問に対応するプログラムを実装せよ. なお,問題37, 38, 39はmatplotlibもしくはGnuplotを用いるとよい.

31. 動詞
動詞の表層形をすべて抽出せよ.


■ Pythonのコード
import re

def analyze():
    lines = []
    sentence = []
    with open('neko.txt.mecab', 'r', encoding='utf8') as fin:
        for line in fin:
            words = re.split(r'\t|,|\n', line)
            if words[0] == 'EOS':
                if sentence:
                    lines.append(sentence)
                    sentence = []
                continue
            sentence.append({
                "surface": words[0],
                "base": words[7],
                "pos": words[1],
                "pos1": words[2],
            })
    return lines

def extractVerbs(lines):
    for sentense in lines:
        seq = filter(lambda w: w['pos'] == '動詞', sentense)
        yield from map(lambda w: w['surface'], seq)

def main():
    article = analyze()
    verbs = [surface for surface in extractVerbs(article)]
    print(verbs[:50])
    print('-----')

if __name__ == '__main__':
    main()

■ ちょっとコードの解説


analyze関数は問題30で書いたものと同じです。このanalyzeの結果を使い、今回書いたextractVerbs関数で、動詞だけを抜き出しています。

キー'pos'の値を見れば、動詞かどうかが判断できます。ちなみに pos は、品詞: part of speech の頭文字だと思われます。

extractVerbs関数は、以下のforとifを使ったコードと同等です。
def extractVerbs(lines):
    for sentense in lines:
        for word in sentense:
            if word['pos'] == '動詞':
                yield word['surface']
上のコードは、for文で回しているだけなので、何も難しいことはやっていません。

抜き出した動詞は、yieldで列挙しています。 今回はfilter関数とmap関数を使って書いてみたのですが、1行に書きたいけど、1行だと長くなっちゃうし読みやすいとは言えないかな。
C#のLINQみたいに、メソッドチェーンできればいいのですが...

でも yield from 構文はすごく便利ですね。これはC#にも欲しい機能です。 yield from 構文が無いと、以下のように書かなくてはなりません。
seq = filter(lambda w: w['pos'] == '動詞', sentense)
seq = map(lambda w: w['surface'], seq)
for w in seq:
    yield w

main関数では、リスト内包表記使ってリストにしています。

■ 結果

取り出した結果の先頭50個を表示しています。
['生れ', 'つか', 'し', '泣い', 'し', 'いる', '始め', '見', '聞く', '捕え', '煮', '食う', '思わ', '載せ', 'られ', '持ち上げ', 'られ', 'し', 'あっ', '落ちつい', '見', '見', '思っ', '残っ', 'いる', 'さ', 'れ', 'し', '逢っ', '出会わ', 'し', 'のみ', 'なら', 'し', 'いる', '吹く', 'せ', '弱っ', '飲む', '知っ', '坐っ', 'おっ', 'する', 'し', '始め', '動く', '動く', '分ら', '廻る', 'なる']