第1章: 準備運動
05. n-gram
与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
単語n-gramと文字n-gramでそれぞれ作る必要がありますが、型が違うだけでほとんど中身は同じなので、文字n-gramの方だけ載せるとこんな感じです。
pub fn character_n_gram(text: &str, n: usize) -> Vec<String> {
let max = text.len() - n + 1;
let mut result = Vec::new();
for i in 0..max {
result.push(text.get(i..(i+n)).unwrap().to_string());
}
result
}
get
で範囲指定が使えるのがすごく便利です。
06. 集合
"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
HashSet
を使えばそれで終わりなので、特別に関数を作らずに直接求めてみました。演算子が使えるのは便利ですね。
let paraparaparadise = prepare::character_n_gram("paraparaparadise", 2).into_iter().collect::<HashSet<String>>();
let paragraph = prepare::character_n_gram("paragraph", 2).into_iter().collect::<HashSet<String>>();
let union = ¶paraparadise | ¶graph;
println!("和集合={:?}, 積集合={:?}, 差集合={:?}, find 'se'={}",
union,
¶paraparadise & ¶graph,
¶paraparadise - ¶graph,
union.is_some()
);
07. テンプレートによる文生成
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.
format!
を使うだけですが、せっかくなのでジェネリクスの練習も兼ねて関数化してみました。
pub fn generate_template_text<X: Display, Y: Display, Z: Display>(x: X, y: Y, z: Z) -> String {
format!("{}時の{}は{}", x, y, z)
}
08. 暗号文
与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
- 英小文字ならば(219 - 文字コード)の文字に置換
- その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.
文字が英小文字の場合、a→z、z→a、となるので同じ関数で暗号化・復号化できます。
pub fn encrypt(text: &str) -> String {
text.chars().map(|c| if c.is_ascii_lowercase() { (219 - c as u8) as char } else { c }).collect()
}
09. Typoglycemia
スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.
第1章最後の問題、少し躓きました。とちゅう、シャッフル部分はrandクレートを使用しています。
まず、文字列を並び替えなければいけないとなると、str
のままだと難しく、さらに文字列の結合をするときは先頭がString
で後ろがstr
という形にしないといけないみたいです。また、String
のスライスがchar
のスライスになってくれればよいのですが、そうはいかずにu8
になってしまうようで一旦Chars
に変換するという発想に至るのが難しかったです(ascii文字のみ想定にすればこの必要はないのですが)。
pub fn typoglycemia(text: &str) -> String {
text.split_whitespace().map(|s| {
if s.len() < 5 {
s.to_string()
} else {
let (head, remaining) = s.split_at(1);
let (body, tail) = remaining.split_at(remaining.len() - 1);
let mut body: Vec<_> = body.chars().collect();
thread_rng().shuffle(&mut body);
head.to_string() + &body.into_iter().collect::<String>() + tail
}
}).fold(String::new(), |result, s| if result == "" { s } else { result + " " + &s })
}
第1章の感想
第1章なので楽かと思いましたが、特に最後はかなり詰まってしまいました。文字列の扱いって簡単そうに見えて(主に文字コード絡みのせいで)ややこしいですよね。