やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのコレクション(練習問題)

 Vec, String, HashMap。

成果物

参考

 以下をヒントに解いた。

コード

Vec

 問題。

整数のリストが与えられ、ベクタを使ってmean(平均値)、median(ソートされた時に真ん中に来る値)、 mode(最も頻繁に出現する値; ハッシュマップがここでは有効活用できるでしょう)を返してください。

 解答。

fn main() {
    let v = vec![5, 2, 7, 1, 3, 2, 9];
    println!("vec: {:?}", v);
    println!("mean: {}", mean(&v));
    println!("median: {}", median(&v));
    println!("mode: {}", mode(&v));
}
// 平均
fn mean(v: &Vec<i32>) -> f64 {
    let mut sum: f64 = 0.0;
    for i in v {
        sum += f64::from(*i); // https://stackoverflow.com/questions/55080089/converting-i32-to-f64
    }
    sum / v.len() as f64
}
// ソート時の中央値
fn median(v: &Vec<i32>) -> i32 {
    let mut s = Vec::new();
    for i in v { s.push(i); }
    s.sort();
    **s.get(s.len()/2).unwrap()
}
// 頻出値
fn mode(v: &Vec<i32>) -> i32 {
    use std::collections::HashMap;
    let mut h = HashMap::new();
    for i in v {
        let count = h.entry(i).or_insert(0);
        *count += 1;
    }
    let mut mode = 0;
    let mut max_count = 0;
    for (key, value) in h {
        if max_count < value { max_count = value; mode = *key; }
    }
    mode
}

 f64::from(*i), as f64など型の変換・統一がポイント。これは未学習だった。ググって知った。

String

 問題。

文字列をピッグ・ラテン(訳注: 英語の言葉遊びの一つ)に変換してください。各単語の最初の子音は、 単語の終端に移り、"ay"が足されます。従って、"first"は"irst-fay"になります。ただし、 母音で始まる単語には、お尻に"hay"が付け足されます("apple"は"apple-hay"になります)。 UTF-8エンコードに関する詳細を心に留めておいてください!

 解答。

fn main() {
    println!("{}", pig_latin("first"));
    println!("{}", pig_latin("apple"));
}
fn pig_latin(s: &str) -> String {
    let mut pig = String::from(s);
    let vowels = ['a','i','u','e','o']; // 母音
    match s.chars().nth(0).unwrap() {
        'a'|'i'|'u'|'e'|'o' => pig.push_str("hay"),
        _ => { pig.push(s.chars().nth(0).unwrap()); pig.remove(0); pig.push_str("ay"); },
    }
    pig
}

 nth()関数がポイント。これまでのドキュメントには出てこなかった。ググって知った。

HashMap

 問題。

ハッシュマップとベクタを使用して、ユーザに会社の部署に雇用者の名前を追加させられるテキストインターフェイスを作ってください。 例えば、"Add Sally to Engineering"(開発部門にサリーを追加)や"Add Amir to Sales"(販売部門にアミールを追加)などです。 それからユーザに、ある部署にいる人間の一覧や部署ごとにアルファベット順で並べ替えられた会社の全人間の一覧を扱わせてあげてください。

 解答。

use std::collections::HashMap;
use std::io::Write;
fn main() {
    let mut employees: HashMap<String, Vec<String>> = HashMap::new();
    loop {
        let mut s = String::new();

        println!("コマンド: quit, add 部署名 従業員名, list [部署名]");
        print!("> ");
        std::io::stdout().flush().unwrap();

        std::io::stdin().read_line(&mut s).ok();
        if !analize_command(&s.trim(), &mut employees) { break; }
    }
}
fn analize_command(s: &str, employees: &mut HashMap<String, Vec<String>>) -> bool {
    let commands: Vec<&str> = s.trim().split(' ').collect();
    match commands[0] {
        "quit" => false,
        "add" if 2 < commands.len() => {
            add_employee(employees, commands[1], commands[2]);
            true
        },
        "list" if 1 == commands.len() => { println!("{:?}", employees); true },
        "list" if 1 < commands.len() => { println!("{:?}", employees.get(commands[1])); true },
        _ => true,
    }
}
fn add_employee(employees: &mut HashMap<String, Vec<String>>, department: &str, employee: &str) {
    employees.entry(department.to_string()).or_insert(Vec::new());
    employees.get_mut(department).unwrap().push(employee.to_string()); // https://users.rust-lang.org/t/hashmap-with-vector-values/17906
}

 HashMapのget_mut()関数がポイント。未学習。ググって知った。flush()も同じ。

所感

 問題を解くのは楽しかった。が、型名やAPIを知らないせいで、つまづきまくった。都度APIリファレンス等をググって何度も書いているうちに覚えるだろう。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで