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

Rustで[言語処理100本ノック](http://www.cl.ecei.tohoku.ac.jp/nlp100/)しています。

# 第4章: 形態素解析
開発環境上にインストールした[mecab](http://taku910.github.io/mecab/)を用いています。辞書も開発環境のパッケージマネージャに用意されているものをそのまま用いています。
[後編はこちら](https://qiita.com/kbone/items/67c6b1c995a7ccfbce8b)

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

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

```rust
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だけ示します。

```rust
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つの名詞が「の」で連結されている名詞句を抽出せよ．

```rust
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の辞書の問題でしょうが、これでいいのか？という答えが出てしまったのですが、とりあえず条件には適合していそうです。

```rust
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
}
```