LoginSignup
2
0

More than 5 years have passed since last update.

なぜかRustで言語処理100本ノック ~第4章 前編~

Last updated at Posted at 2018-10-27

Rustで言語処理100本ノックしています。

第4章: 形態素解析

開発環境上にインストールしたmecabを用いています。辞書も開発環境のパッケージマネージャに用意されているものをそのまま用いています。
後編はこちら

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

形態素解析結果(neko.txt.mecab)を読み込むプログラムを実装せよ.ただし,各形態素は表層形(surface),基本形(base),品詞(pos),品詞細分類1(pos1)をキーとするマッピング型に格納し,1文を形態素(マッピング型)のリストとして表現せよ.第4章の残りの問題では,ここで作ったプログラムを活用せよ.

mecabの出力結果は表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音と並んでいます。なのでここから各要素を取り出してHashMapに格納し、文ごとにそれをVecに格納すればよいです。

pub fn read_mecab(path: &Path) -> Result<Vec<Vec<HashMap<String, String>>>> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let mut lines = br.lines();
    let mut results = Vec::new();
    let mut sentence = Vec::new();
    while let Some(Ok(line)) = lines.next() {
        if line == "EOS" {
            results.push(sentence);
            sentence = Vec::new();
        } else {
            let mut elements = HashMap::new();
            let line = line.replace("\t", ",");
            let tmp: Vec<_> = line.split_terminator(',').collect();
            elements.insert("surface".to_string(), tmp[0].to_string());
            elements.insert("base".to_string(), tmp[7].to_string());
            elements.insert("pos".to_string(), tmp[1].to_string());
            elements.insert("pos1".to_string(), tmp[2].to_string());
            sentence.push(elements);
        }
    }
    Ok(results)
}

31. 動詞、32. 動詞の原形、33. サ変名詞

動詞の表層形をすべて抽出せよ.
動詞の原形をすべて抽出せよ.
サ変接続の名詞をすべて抽出せよ.

この3つはとても単純かつよく似ているので31だけ示します。

pub fn get_verb_surfaces(data: &Vec<Vec<HashMap<String, String>>>) -> Vec<String> {
    data.iter()
    .flat_map(|sentence| sentence.into_iter()
                                 .filter(|elements| elements["pos"] == "動詞")
                                 .map(|elements| elements["surface"].clone()))
    .collect()
}

34. 「AのB」

2つの名詞が「の」で連結されている名詞句を抽出せよ.

pub fn a_no_b(data: &Vec<Vec<HashMap<String, String>>>) -> Vec<String> {
    data.iter().filter(|sentence| sentence.len() > 2).flat_map(|sentence| {
        let mut results = Vec::new();
        for i in 0..(sentence.len() - 2) {
            if sentence[i + 1]["surface"] == "の"
                && sentence[i]["pos"] == "名詞"
                && sentence[i + 2]["pos"] == "名詞" {
                results.push(sentence[i]["surface"].clone()
                             + "の" + &sentence[i + 2]["surface"]);
            }
        }
        results
    }).collect()
}

前章が難しかったせいかここまではとても簡単に感じます。

35. 名詞の連接

名詞の連接(連続して出現する名詞)を最長一致で抽出せよ.

あまり綺麗に書けなかったのに加えて、これはインストールしたmecabの辞書の問題でしょうが、これでいいのか?という答えが出てしまったのですが、とりあえず条件には適合していそうです。

pub fn longest_noun(data: &Vec<Vec<HashMap<String, String>>>) -> String {
    let mut longest = String::new();
    let mut max_length = 0;
    let mut tmp = String::new();
    let mut current_length = 0;
    data.into_iter().for_each(|sentence| {
        let mut buffer = String::new();
        if !tmp.is_empty() {
            buffer.push_str(&tmp);
            tmp = String::new();
        }
        sentence.into_iter().for_each(|elements| {
            if elements["pos"] == "名詞" {
                buffer.push_str(&elements["surface"]);
                current_length += 1;
            } else {
                if current_length > max_length {
                    max_length = current_length;
                    longest = buffer.clone();
                }
                current_length = 0;
                buffer = String::new();
            }
        });
        if !buffer.is_empty() {
            tmp = buffer;
        }
    });
    longest
}
2
0
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
2
0