Rustで100本ノック【1】

にあえん

August 20, 2023

ろくにRustに触らないままでwasmをやってしまっていましたが、ちゃんと触っておこうと思って100本ノックに挑みました。

言語処理100本ノック 2015

その際の備忘録を残します。

Q00: 文字列の逆順

逆順にするだけなのに、結局ググってしまった。。

Conversion between String, str, Vec<u8>, Vec<char> in Rust · GitHub

fn main() {
    // 文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.
    let s = "stressed";
    let mut s = s.chars().collect::<Vec<_>>();
    s.reverse();
    print!("{}", s.iter().collect::<String>());
}

collectの使い方とか、pythonみたいに容易に行かない感じがする。

Q01

さっきよりかはスムーズにできた。

でもイテレータの使い方とかちょっとググった。

Rustの配列(array)の使い方 - Yunix Blog

fn main() {
    // 「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.
    let s = "パタトクカシーー";
    for (i, c) in s.chars().enumerate() {
        if !([1,3,5,7].contains(&i)) {
            continue;
        }
        print!("{}", c);
    }
    println!();
}

Q02

基本的に先程の2問と解き方は一緒だった。

fn main() {
    // 「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.
    let s1 = "パトカー";
    let s2 = "タクシー";

    let mut s1_vec = s1.chars().collect::<Vec<_>>();
    let mut s2_vec = s2.chars().collect::<Vec<_>>();

    s1_vec.reverse();
    s2_vec.reverse();

    let mut result: Vec<char> = vec![];
    while s1_vec.len() > 0 && s2_vec.len() > 0 {
        if s1_vec.len() > 0 {
            result.push(s1_vec.pop().unwrap());
        }
        if s2_vec.len() > 0 {
            result.push(s2_vec.pop().unwrap());
        }
    }

    println!("{}", result.iter().collect::<String>());
}

Q03

前の問題に比べると少し楽になりました。

fn main() {
    // "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.
    let s1 = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.";

    let mut results: Vec<char> = vec![];
    for a in s1.split(" ").collect::<Vec<_>>() {
        results.push(a.chars().collect::<Vec<char>>()[0]);
    }
    println!("{}", results.iter().collect::<String>())
}

Q04

iter()iter_into()の違いに軽く触れた。

iter()とinto_iter()の違いを整理した - さんちゃのblog

ぶっちゃけそこまでちゃんと分かったわけではないけど、なんとなく分かった気になった。

use std::collections::HashMap;

fn main() {
    // "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,
    // 1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,
    // 取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.
    let s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.";

    let mut results: Vec<(usize, String)> = vec![];
    for (i, a) in s.split(" ").collect::<Vec<_>>().iter().enumerate() {
        let result = if [1, 5, 6, 7, 8, 9, 15, 16, 19].contains(&(i + 1)) {
            a.chars().collect::<Vec<char>>()[0..2].iter().collect::<String>()
        } else {
            a.chars().collect::<Vec<char>>()[0].to_string()
        };

        results.push((i, result));
    }

    let hashmap = results.into_iter().collect::<HashMap<usize,String>>();
}

Q05

n-gramってそもそも何?って感じだったので、調べてからスタートした。

組み合わせみたいなもの?

N-gram &#8211; 【AI・機械学習用語集】

単語のn-gramは以下に説明がありました。

自然言語処理に出てくるn-gramとはなに? | もじとばコム

感覚的に解いたけど、本当にこれで合ってるかな…?

あとAsRefを使うと文字列として扱える型がいろいろ扱えていい感じになります。

AsRef&lt;T&gt;|Rust の文字列の種類と変換トレイトについて

fn main() {
    // 与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
    let seq = "I am an NLPer";

    get_char_bigram(seq);
}

fn get_char_bigram<T: AsRef<str>>(seq: T) {
    let s = seq.as_ref();

    let word_list = s.split(" ").collect::<Vec<&str>>();
    let char_list = s.replace(" ", "").chars().collect::<Vec<char>>();

    let mut word_bigram: Vec<(&str, &str)> = vec![];
    for (i, word1) in word_list.iter().enumerate() {
        for word2 in word_list.get(i + 1..).unwrap().iter() {
            word_bigram.push((word1, word2));
        }
    }

    let mut char_bigram: Vec<(char, char)> = vec![];
    for (i, char1) in char_list.iter().enumerate() {
        for char2 in char_list.get(i + 1..).unwrap().iter() {
            char_bigram.push((*char1, *char2));
        }
    }
}

Q06

和集合とか差集合とか完全に忘れていたので、調べてから実装しました。

和集合と積集合の意味と公式 - 具体例で学ぶ数学

���W���Ƃ� - �Ӗ����킩���₷�� - IT�p�ꎫ�T e-Words

結構行数が長くなってしまった。。

多分これで合ってるよね…?

fn main() {
    // "paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.
    // さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
    let s1 = "paraparaparadise";
    let s2 = "paragraph";

    let s1_bigram = get_char_bigram(s1);
    let s2_bigram = get_char_bigram(s2);

    
    let sum_collect = sum_set(&s1_bigram, &s2_bigram);
    println!("sum_collect: {:?}", sum_collect);
    let diff_collect = difference_set(&s1_bigram, &s2_bigram);
    println!("diff_collect: {:?}", diff_collect);
    let integral_collect = integral_sets(&s1_bigram, &s2_bigram);
    println!("integral_collect: {:?}", integral_collect);

    // 'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
    let s1_in_se = s1_bigram.iter().map(|(c1, c2)| format!("{}{}", c1, c2)).find(|s| s == "se").is_some();
    println!("s1 in se?: {}", s1_in_se);
    let s2_in_se = s2_bigram.iter().map(|(c1, c2)| format!("{}{}", c1, c2)).find(|s| s == "se").is_some();
    println!("s2 in se?: {}", s2_in_se);
}

fn difference_set(list1: &Vec<(char, char)>, list2: &Vec<(char, char)>) -> Vec<(char, char)> {
    let mut diff_collect: Vec<(char, char)> = vec![];
    for t in list1 {
        if list2.iter().find(|&&x| x.0 == t.0 && x.1 == t.1).is_some() {
            diff_collect.push(*t);
        }
    }

    diff_collect
}

fn sum_set(list1: &Vec<(char, char)>, list2: &Vec<(char, char)>) -> Vec<(char, char)> {
    let mut sum_collect: Vec<(char, char)> = vec![];
    sum_collect.extend(list1);
    sum_collect.extend(list2);

    sum_collect
}
fn integral_sets(list1: &Vec<(char, char)>, list2: &Vec<(char, char)>) -> Vec<(char, char)> {
    let mut integral_collect: Vec<(char, char)> = sum_set(list1, list2);

    integral_collect.sort_by(|a, b| format!("{}{}", a.0, a.1).cmp(&format!("{}{}", b.0, b.1)));
    integral_collect.dedup();

    return integral_collect;
}

fn get_char_bigram(seq: &str) -> Vec<(char, char)> {
    let char_list = seq.chars().collect::<Vec<char>>();
    let mut char_bigram: Vec<(char, char)> = vec![];
    for (i, char1) in char_list.iter().enumerate() {
        for char2 in char_list.get(i + 1..).unwrap().iter() {
            char_bigram.push((*char1, *char2));
        }
    }

    char_bigram
}

Q07

急にめっちゃ簡単になった。

今まで計算するだけでprintしなくてもいい問題だったからかな?

fn main() {
    // 引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.
    print!("{}", gen_str("12", "気温", "22.4"));
}

fn gen_str(x: &str, y: &str, z: &str) -> String {
    format!("{}時の{}{}", x, y, z)
}

Q08

暗号化と復号化の問題。

最初一つの関数だけで暗号化も復号化もできるのか…?と気になったけど、できた。

こういう問題は面白いですね。

fn main() {
    // 与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
    // - 英小文字ならば(219 - 文字コード)の文字に置換
    // - その他の文字はそのまま出力
    // この関数を用い,英語のメッセージを暗号化・復号化せよ.
    let encoded_str = cipher("Hello, world!");
    println!("{}", encoded_str);
    let decoded_str = cipher(encoded_str.as_str());
    println!("{}", decoded_str);
}

fn cipher(s: &str) -> String {
    let mut result: Vec<u8> = vec![];
    for c in s.bytes() {
        if c.is_ascii_lowercase() {
            result.push((219 - c));
        } else {
            result.push(c);
        }
    }

    String::from_utf8(result).unwrap()
}

Q09

結構時間かけてやった。

ランダムな並べ替えにどういうアルゴリズムを使うか、とか、配列の並べ替えが他の言語とちょっと違う(貸借システムのせい)ので、そのへんで時間を使いました。

使用したランダムアルゴリズムの説明は以下。

ランダムシャッフル | Programming Place Plus アルゴリズムとデータ構造編【その他のアルゴリズム】 第2章

use rand::Rng;

fn main() {
    // スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.
    // ただし,長さが4以下の単語は並び替えないこととする.
    // 適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.
    let sentence = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .";
    let result = sentence
        .split(" ")
        .map(|word| {
            if word.len() > 4 {
                swap_random(&word[1..word.len() - 1])
            } else {
                word.to_string()
            }
        }).collect::<Vec<String>>()
        .join(" ");

        println!("{}", result);
}

fn swap_random(target_str: &str) -> String {
    let mut rng = rand::thread_rng();
    let mut target_chars: Vec<char> = target_str.chars().collect();
    let len = target_chars.len();

    for i in 0..len {
        let random_i = rng.gen_range(0..len);
        target_chars.swap(i, random_i);
    }

    target_chars.iter().collect()
}

まとめ

とりあえず10問はできました。(結構時間かかったけど)

あと90問!!頑張るぞー