<style>
/* basic design */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6,
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {
font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic';
text-align: left;
line-height: 1.6;
letter-spacing: normal;
text-shadow: none;
word-wrap: break-word;
color: #444;
}
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;}
.reveal h1, .reveal h2, .reveal h3 {color: #2980b9;}
.reveal th {background: #DDD;}
.reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;}
.reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;}
.reveal table {margin: 1.0vw auto;}
.reveal code {line-height: 1.2;}
.reveal p, .reveal li {padding: 0vw; margin: 0vw;}
.reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;}
/* table design */
.reveal table {background: #f5f5f5;}
.reveal th {background: #444; color: #fff;}
.reveal td {position: relative; transition: all 300ms;}
.reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;}
.reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;}
/* blockquote design */
.reveal blockquote {
width: 90%;
padding: 0.5vw 0 0.5vw 6.0vw;
font-style: italic;
background: #f5f5f5;
}
.reveal blockquote:before{
position: absolute;
top: 0.1vw;
left: 1vw;
content: "\f10d";
font-family: FontAwesome;
color: #2980b9;
font-size: 3.0vw;
}
/* font size */
.reveal h1 {font-size: 5.0vw;}
.reveal h2 {font-size: 4.0vw;}
.reveal h3 {font-size: 2.8vw;}
.reveal h4 {font-size: 2.6vw;}
.reveal h5 {font-size: 2.4vw;}
.reveal h6 {font-size: 2.2vw;}
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.2vw;}
.reveal code {font-size: 1.6vw;}
/* new color */
.red {color: #EE6557;}
.blue {color: #16A6B6;}
/* split slide */
#right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;}
#left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;}
</style>
<style>
/* specific design */
.reveal h2 {
padding: 0 1.5vw;
margin: 0.0vw 0 2.0vw -2.0vw;
border-left: solid 1.2vw #2980b9;
border-bottom: solid 0.8vw #d7d7d7;
}
</style>
<!-- --------------------------------------------------------------------------------------- -->
# Rust勉強会 第7回
## 8章 一般的なコレクション
### 2020/9/18 岡本拓海
---
## 本日のメニュー
1. ベクタ型 ```Vec```
2. 文字列型 ```String```
3. ハッシュマップ型 ```std::collection::Hashmap```
4. 練習問題
プレゼン20分くらい+議論10分位で18:30前後終了を目指します。
---
## ベクタ型 Vec
配列がオブジェクト(トレイト?)になったと思えば良いです。
APIリファレンスには
>A contiguous growable array type, written Vec\<T\> but pronounced 'vector'
と書いてあり"ベクター"と読むそうです
----
## ベクタ型の基本的な使い方
```rust
let v = vec![1,2,3]; // マクロで生成
let mut v = Vec::new(); // Vecのメンバ関数で生成
v.push(5); // push で値を追加
v.push(6);
// 添字記法かgetメソッドを使用してベクタの要素にアクセス
let third: &i32 = &v[2];
let third: Option<&i32> = v.get(2);
```
----
## for で走査する
```rust
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50; // *は参照外し
}
```
----
## Enumで複数の型を保持することもできる
```rust
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
```
forで回してmatchとかする使い方できそう
----
## 範囲外を読むとどうなるか
```rust
let v = vec![1, 2, 3, 4, 5];
let does_not_exist = &v[100]; // -> panic !!!
let does_not_exist = v.get(100); // -> None Optionが返る
```
----
## ベクタをドロップすると要素もドロップする
```rust
{
let v = vec![1, 2, 3, 4];
// vで作業をする
} // <- vはここでスコープを抜け、解放される
```
----
## 一度不変にしたら不変
```rust
let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];
v.push(6);// -> エラー!!上で一度不変借用してるので可変借用できない
println!("The first element is: {}", first);
```
----
## APIを読みましょう
pop , clear , as_slice , append, remove , len ...etc
一般的なコレクションにあるメソッドはだいたいありそうです。
---
## 文字列型 String
文字列でUTF-8でエンコードされたテキストを保持する
----
## String or &str or ??
- String : 文字列型
- &str : 文字列スライス
他にもCStringとかOsStringとか色々あるので詳しくはAPIで
----
## Stringの基本的な使い方
```rust
let mut s = String::new();
let hello = "नमस्ते".to_string();// 文字列リテラルから生成;
let hello = String::from("Ciao")
let hello = String::from("こんにちは");
let mut s = String::from("foo");
s.push_str("bar");// 文字列スライスを足す
s.push('l'); //pushで足せるのは'文字'
```
----
## 文字列の連結
```rust
let one = String::from("Hello, ");
let two = String::from("world!");
let s = one + &two; // oneはムーブされ、もう使用できないことに注意
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
// 2個以上ならformat!が使いやすい
let s = format!("{}-{}-{}", s1, s2, s3);
```
----
## 文字列は添字アクセスできない!
```rust
let s1 = String::from("hello");
let h = s1[0]; // <- コンパイルエラー
let hello = "Здравствуйте"; // &strに範囲アクセスならOK
let s = &hello[0..4]; // Зд
let s = &hello[0..1]; // panic! 2バイト文字の頭1バイトのみにアクセス
```
[この辺](https://doc.rust-jp.rs/book/second-edition/ch08-02-strings.html#a%E5%86%85%E9%83%A8%E8%A1%A8%E7%8F%BE)に色々書いてます。
2バイト文字の扱いが難しい等の理由があるようです。
----
## 文字列の走査
```rust
for c in "नमस्ते".chars() {
println!("{}", c); // 文字を一つづつ出力
}
for b in "नमस्ते".bytes() {
println!("{}", b); //文字コード出力
}
```
----
## 文字列はややこしい!!!
> Rustでは、Stringデータを正しく扱うことが、全てのRustプログラムにとっての既定動作になっているわけであり、 これは、プログラマがUTF-8データを素直に扱う際に、よりしっかり考えないといけないことを意味します。 このトレードオフにより、他のプログラミング言語で見えるよりも文字列の複雑性がより露出していますが、 ASCII以外の文字に関するエラーを開発の後半で扱わなければならない可能性が排除されているのです。
---
## ハッシュマップ HashMap<K,V>
HashMap<K, V>は、 K型のキーとV型の値の対応関係を保持します。
他の言語で、辞書型とか連想配列とか言われたりするヤツです。
----
## ハッシュマップの基本的な使い方
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// vectorから作ることもできます
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
let team_name = String::from("Blue");
let score = scores.get(&team_name); //getで値にアクセス
//for ループも回せます
for (key, value) in &scores {
println!("{}: {}", key, value);
}
```
----
## 値の上書き
```rust
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25); //上書き
// entryでキーの存在をチェック
// or_insertはキーが無かったときのみデータを挿入する
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores); // {"Blue": 25}
```
----
## 古い値に基づいて更新する
```rust
use std::collections::HashMap;
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
// or_insertはキーに対する値の可変参照を返す
let count = map.entry(word).or_insert(0);
//count にはmapのエントリに対する可変参照が入ってる
*count += 1;
}
println!("{:?}", map);
```
----
## ハッシュ関数
BuildHasherトレイトを実装した型を使えば独自のハッシュ関数を使えます。
(オレオレハッシュ関数はあまり感心しませんが。。。)
---
# 練習問題
章末に練習問題が付いています
---
## 練習問題1
整数のリストが与えられ、ベクタを使って
* mean(平均値)
* median(ソートされた時に真ん中に来る値)
* mode(最も頻繁に出現する値; ハッシュマップがここでは有効活用できるでしょう)
を返してください。
----
### 回答例
```rust
use std::collections::HashMap;
/// 平均
pub fn mean(data: &Vec<i32>) -> f64 {
let mut sum = 0;
for datum in data.iter() {
sum += datum;
}
sum as f64 / data.len() as f64
}
/// 中央値
pub fn median(data: &Vec<i32>) -> i32 {
let mut clone_data = data.clone();
clone_data.sort();
let x :f64 = clone_data.len() as f64 /2.0_f64.floor();
clone_data[x as usize]
}
/// 最瀕値
pub fn mode(data: &Vec<i32>) -> i32 {
let mut counts: HashMap<i32,i32> = HashMap::new();
let mut max_count = 0;
let mut mode: &i32 = &0;
for datum in data.iter(){
let counter = counts.entry(*datum).or_insert(0);
*counter += 1;
if *counter > max_count{
max_count = *counter;
mode = datum;
}
}
println!("{:?}",counts);
println!("{:?}",data);
println!("mode {:?}", mode);
*mode
}
// 11章を参照するとユニットテストはここが良いみたい
#[cfg(test)]
mod tests {
use super::*;
use rand::prelude::*;
#[test]
fn test_mean() {
let array = vec![1,2,3,4,5,6,7,8,9,10];
assert_eq!(mean(&array), 5.5);
}
#[test]
fn test_median() {
let mut rng = rand::thread_rng();
let mut array = vec![0,1,2,3,4,5,6,7,8,9,10];
array.shuffle(&mut rng);
assert_eq!(median(&array),5);
}
#[test]
fn test_mode() {
let mut array = Vec::new();
for i in 0..=10 as i32 {
for _ in 0..=i as i32 {
array.push(i);
}
}
let mut rng = rand::thread_rng();
array.shuffle(&mut rng);
assert_eq!(mode(&array),10);
}
}
```
---
## 練習問題2
文字列をピッグ・ラテン(訳注: 英語の言葉遊びの一つ)に変換してください。各単語の最初の子音は、 単語の終端に移り、"ay"が足されます。従って、"first"は"irst-fay"になります。ただし、 母音で始まる単語には、お尻に"hay"が付け足されます("apple"は"apple-hay"になります)。 UTF-8エンコードに関する詳細を心に留めておいてください!
----
### 回答例
```rust
fn is_vowel(c : char) -> bool {
c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'
|| c == 'A' || c == 'E'|| c == 'I' || c == 'O' || c == 'U'
}
pub fn get_pig_latin_word(word: &str) -> String{
// 母音が頭文字の場合
if word.starts_with(is_vowel){
return word.to_string() + &String::from("hay");
}
let mut word_vec :Vec<char> = word.clone().chars().collect();
// 大文字が頭にあった場合
if word.starts_with(char::is_uppercase) {
// 1文字目を小文字 2文字目を大文字に
word_vec[0] = word_vec[0].to_ascii_lowercase();
word_vec[1] = word_vec[1].to_ascii_uppercase();
}
let s :String = word_vec.into_iter().collect();
let word = &s;
// 頭1文字子音切り出し + 末尾にay
[ &word[1..],&word[..1],"ay"].concat().to_string()
}
pub fn translate(text: &String) -> String {
let mut translated_string = String::new();
for mut word_option in text.split_ascii_whitespace(){
let mut terminal : Option<&str> = None;
//'.','?','!',':',','で終わっていた場合の処理
if word_option.ends_with(".") {
// word_optionから末尾を取り除く
println!("word_option :: {:?}",word_option);
let end = word_option.len()-1;
let t = &word_option[end..];
word_option = &word_option[..end];
println!("t :: {:?}",t);
terminal = Some(t);
}
let word = get_pig_latin_word(word_option);
translated_string += &word;
match terminal {
Some(e) => {
println!("e :: {:?}",e);
translated_string.push_str(e)
},
None => translated_string.push_str(" "),
}
}
translated_string
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ascii() {
let ascii = 'a';
let non_ascii = '❤';
let q = '?';
assert!(ascii.is_ascii());
assert!(q.is_ascii());
assert!(!q.is_ascii_alphabetic());
assert!(!q.is_alphabetic());
assert!(!non_ascii.is_ascii());
}
#[test]
fn test_string() {
assert_eq!(String::from("This is a pen."),String::from("This is a pen."));
}
#[test]
fn test_translate() {
assert_eq!(translate(&String::from("I don't understand.")),
String::from("Ihay on'tday understandhay."));
assert_eq!(translate(&String::from("This is a pen.")),
String::from("Histay ishay ahay enpay."));
assert_eq!(translate(&String::from("Good morning")),
String::from("Oodgay orningmay "));
}
#[test]
fn test_get_pig_latin_word(){
assert_eq!(get_pig_latin_word("I"),"Ihay");
assert_eq!(get_pig_latin_word("understand"),"understandhay");
assert_eq!(get_pig_latin_word("don't"),"on'tday");
assert_eq!(get_pig_latin_word("good"),"oodgay");
assert_eq!(get_pig_latin_word("morning"),"orningmay");
assert_eq!(get_pig_latin_word("Hello"),"Ellohay");
assert_eq!(get_pig_latin_word("first"),"irstfay");
assert_eq!(get_pig_latin_word("First"),"Irstfay");
assert_eq!(get_pig_latin_word("apple"),"applehay");
assert_eq!(get_pig_latin_word("Apple"),"Applehay");
}
}
```
---
## 練習問題3
ハッシュマップとベクタを使用して、ユーザに会社の部署に雇用者の名前を追加させられるテキストインターフェイスを作ってください。
例えば、"Add Sally to Engineering"(開発部門にサリーを追加)や"Add Amir to Sales"(販売部門にアミールを追加)などです。
それからユーザに、ある部署にいる人間の一覧や部署ごとにアルファベット順で並べ替えられた会社の全人間の一覧を扱わせてあげてください。
----
## 回答例
```rust
use std::collections::HashMap;
type Employees = HashMap<String,String>;
fn add(employees :&mut Employees,command:&str){
let obj :Vec<&str>= command.splitn(2," to ").collect();
println!("add {:?} as {:?}",obj[0].to_string(), obj[1].to_string());
employees.insert(obj[0].to_string(), obj[1].to_string());
}
fn list(employees :&Employees,command:&str){
if employees.is_empty() {
println!("No Data");
return ()
}
println!("list {:?}",command);
if command == "All" {
for name in employees.keys(){
println!("{:?}",name);
}
return ()
}
for (name , depart) in employees.iter(){
if command == depart {
println!("{:?}",name);
}
}
}
fn help(){
println!("**********This is help************")
}
pub fn command(employees :&mut Employees,command_string: String) -> bool{
let command: Vec<&str> = command_string.splitn(2,' ').collect();
println!("{:?}",command[0]);
match command[0].trim() {
"Exit" => false,
"Add" => {
if command.len() > 1 {
add(employees,command[1].trim());
}
true
},
"List" => {
if command.len() > 1 {
list(employees,command[1].trim());
}
true
},
_ => {
help();
true
},
}
}
use std::io;
pub fn run(){
let mut employees = Employees::new();
loop {
println!("command? >>");
let mut command_string = String::new();
io::stdin().read_line(&mut command_string)
.expect("Failed to read line");
if ! command(&mut employees, command_string) {
break;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command() {
let mut employees = Employees::new();
assert_eq!(command(&mut employees,"Add a to B".to_string()),true);
assert_eq!(command(&mut employees,"List All".to_string()),true);
assert_eq!(command(&mut employees,"List B".to_string()),true);
assert_eq!(command(&mut employees,"Exit".to_string()),false);
assert_eq!(command(&mut employees,"".to_string()),true);
}
#[test]
fn test_add(){
let mut employees = Employees::new();
add(&mut employees,&"a to B".to_string());
println!("{:?}",employees);
assert_eq!(employees.get(&"a".to_string()), Some(&"B".to_string()));
}
}
```
---
## 参考資料
https://doc.rust-jp.rs/book/second-edition/ch08-00-common-collections.html
練習問題の回答↓私のリポジトリです。:star:ください
https://github.com/MrBearing/TheRustProgrammingLanguage/tree/master/projects/ch08/excercise
---
# ご清聴ありがとうございました
{"metaMigratedAt":"2023-06-15T12:45:49.759Z","metaMigratedFrom":"YAML","title":"第7回 8章 一般的なコレクション","breaks":true,"description":"Rust勉強会第7回のスライド","slideOptions":"{\"theme\":\"white\",\"slideNumber\":\"c/t\",\"center\":false,\"transition\":\"none\",\"keyboard\":true}","contributors":"[{\"id\":\"610cfd52-ad2b-46e8-81e2-a79a85a7f06f\",\"add\":20057,\"del\":4379}]"}