Rust 練習問題「ループなしのナベアツ」 map, collect
概要
- 前回、ナベアツ処理を記述する際には「for」文で繰り返し処理を行った。
- 範囲(1..=99)に対して「map」を使えば for 文が不要になるのでは、という試み。
- for, loop, while を使わずに、ナベアツを実現してみよう。
仕様
- 1 からカウントアップしていく。「1」「2」
- 3 の倍数のときアホになる。「3🤪」...「11」「12🤪」
- 数字に 3 が含まれるときもアホになる。「13🤪」
- 100 になったら「オモロー」と言って終わる。「100😲オモロー!」
プログラム例
fn main() { print!("{}", (1..=99).map(ナベアツ変換).collect::<String>()); println!("100😲オモロー!"); } fn ナベアツ変換(n: i32) -> String { n.to_string() + match n % 3 == 0 || n % 10 == 3 || n / 10 == 3 { true => "🤪\n", false => "\n" } }
解説
fn main() { print!("{}", (1..=99).map(ナベアツ変換).collect::<String>()); // └ 1から99までを「ナベアツ変換」して文字列を作り出し、 // └ それを collect でひとつの文字列に結合し、 // └ それをprint!で出力する。 println!("100😲オモロー!"); } fn ナベアツ変換(n: i32) -> String { n.to_string() + // 数値を文字列に変換 match n % 3 == 0 || n % 10 == 3 || n / 10 == 3 { true => "🤪\n", // 3で割り切れる・一の位が3・十の位が3 false => "\n" // それ以外 } }
Rust 配列の操作 iter, cloned, collect, filter, map
概要
- 配列の中身をひとつずつ取り出すには iter() を使う。
- 取り出した結果を新しい配列にまとめるには cloned(), collect() を使う。
- 取り出したい中身を限定するには filter() を使う。
- 取り出した中身に変更を加えるには map() を使う。
プログラム例
- 配列を入力にして、全く同じ配列を出力する。つまりコピー。
- iter().cloned(), collect::<Vec<_>>() を使う。
fn main() { let 配列 = vec![1, 2, 3, 4]; let 配列2 = 配列.iter().cloned() // ひとつずつ中身を並べる .collect::<Vec<_>>(); // それらを集めて配列にする println!("{:?}", 配列2); }
プログラム例2
- 配列を入力にして、0 を取り除いた配列を出力する。
- filter(|&入力変数| bool値) を使う。
fn main() { let 配列 = vec![1, 0, 2, 0, 3, 0, 0, 4]; let 配列2 = 配列.iter().cloned() // ひとつずつ中身を並べる .filter(|&i| i != 0) // 0でないものだけ残す .collect::<Vec<_>>(); // それらを集めて配列にする println!("{:?}", 配列2); }
プログラム例3
- 配列を入力にして、100未満のものは0にした配列を出力する。
- map(|入力変数| 出力する値) を使う。
fn main() { let 配列 = vec![1, 2, 300, 100, 200, 3]; let 配列2 = 配列.iter().cloned() // ひとつずつ中身を並べる .map(|i| { // iを入力にして if i < 100 {0} // 100未満なら0 else {i} // それ以外ならiに変換(マッピング)する }) .collect::<Vec<_>>(); // それらを集めて配列にする println!("{:?}", 配列2); }
プログラム例4
- 1から10の範囲を入力にして、3の倍数をゼロにした配列を出力する。
fn main() { let 配列 = (1..=10) .map(|i| { // iを入力にして if i % 3 == 0 {0} // 3の倍数なら0 else {i} // それ以外ならiに変換(マッピング)する }) .collect::<Vec<_>>(); // それらを集めて配列にする println!("{:?}", 配列); }
Rust 配列の使い方 vec!, iter, enumerate
概要
- 同じ型の変数を大量に作る代わりに、配列を使うと便利。
- 配列には固定配列と伸長可能な配列の2種類がある。
- for文で今何番目を処理しているか知りたいときは「iter().enumerate()」を使う。
配列を使う場面
- 5つの変数に入った数値を足し合わせるプログラムを考える。
fn main() { let 数0 = 3; let 数1 = 1; let 数2 = 4; let 数3 = 1; let 数4 = 5; println!("{}", 数0 + 数1 + 数2 + 数3 + 数4); }
- 今回は5つ変数を用意したが、配列を使うとスッキリする。
fn main() { let 配列 = [3, 1, 4, 1, 5]; println!("{}", 配列[0] + 配列[1] + 配列[2] + 配列[3] + 配列[4]); }
- 大量の数の合計を計算する場合も、ループで回せば問題ない。
fn main() { let 配列 = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9]; let mut 合計 = 0; for n in 配列 { 合計 += n; } println!("{}", 合計); }
伸長可能な配列 vec!
- 上で説明した配列は、途中で長さを変えることができない。
- 伸長可能な配列を作るには「vec!」を使う。
- よほど速度に制約がない限りはこちらを使うとよさそう。
fn main() { let mut 配列 = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9]; // └ あとで編集するので mut をつける。 配列.push(3); // push(...)で末尾に追加できる let mut 合計 = 0; for n in 配列 { 合計 += n; } println!("{}", 合計); }
配列の繰り返し処理で「今、何番目?」
- 繰り返し文「for n in 配列 {...}」では、今何番目の値を処理しているのか分からない。
- 何番目なのかを得るためには「iter().enumerate()」を使う。
fn main() { let 配列 = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9]; for i in 配列.iter().enumerate() { println!("{}番目は{}", i.0, i.1); // └ i.0 に何番目かという情報が入っている。 } }
Rust 文字列の長さ・分割・切り出し
概要
- 文字列の操作方法を紹介する。
文字列の長さを求める
- 文字列のまま操作する場合は chars().count()
- 文字配列について操作する場合は len()
fn main() { let 文字列 = "あいうえおかきくけこさしすせそ"; println!("{}", 文字列.chars().count()); let 文字配列: Vec<char> = 文字列.chars().collect(); println!("{}", 文字配列.len()); }
文字列を指定文字で分割する
- つまり「aaa@bbb」を「@」で分割して「aaa」と「bbb」を得る操作。
- 配列を得る場合には、すこし記述が長くなる。
fn main() { let 文字列 = "あいうえおかき-くけこさしすせそ"; // 配列にする必要がある場合 let 配列 = 文字列.split('-').fold(Vec::new(), |mut s, i| { s.push(i.to_string()); s }); println!("{:?}", 配列); // 配列にする必要がない場合 for 分割 in 文字列.split('-') { println!("{}", 分割); } }
文字列を切り出す
- 「アイウエオ」の1~3を切り出すと「イウエ」
- 短く表現したが、結構複雑なので関数にまとめた。
fn main() { let 文字列 = "あいうえおかきくけこさしすせそ"; println!("{}", 切り出し(&文字列, 3, 5)); } fn 切り出し(文字列: &str, 開始: usize, 終了: usize) -> String { let mut s = String::new(); for i in 文字列.chars().enumerate() { match i.0 { n if n < 開始 => {continue} n if n > 終了 => {break} _ => {s.push(i.1)} } } s }
補足
- 1文字が1バイトだったり4バイトだったりするので、単純な操作もけっこう面倒である。
Rust 文字列のアクセス chars, nth, collect, []
概要
- 文字列のn番目にアクセスするには nth(数) を使う。
- 長い文字列ほどアクセスに時間がかかる。
- char の配列にするという手もある。
プログラム例
- 文字列「あいうえおかきくけこさしすせそ」について。
- 10番目の文字を表示する。
fn main() { let 文字列 = "あいうえおかきくけこさしすせそ"; match 文字列.chars().nth(10) { Some(文字) => println!("{}", 文字), _ => () } }
解説
- nth(10) で文字列の10番目を辿っている。
- 10番目というと「こ」になりそうだが、実際には「さ」。
- 0番目が先頭なので、添え字と文字の関係は以下のようになる。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
あ | い | う | え | お | か | き | く | け | こ | さ | し | す | せ | そ | (無し) |
- 文字によってデータの長さが違う。英数字=1、ロシア語=2、日本語=3(バイト)。
- そのため、n文字目が何かを調べるたびに、先頭から調べていく必要がある。
- n文字目が先頭から遠いほど、アクセスが遅くなる。
ランダムな場所にアクセスする場合
- すべての文字を char(長さ8バイト)の配列に入れなおす。この作業は遅い。
- 一度やってしまえば、どの文字も長さ8なので、指定の場所に一瞬でアクセスできる。
fn main() { let 文字列 = "あいうえおかきくけこさしすせそ"; let 文字配列: Vec<char> = 文字列.chars().collect(); println!("{}", 文字配列[10]); }
先頭から順にアクセスする場合
- わざわざすべての文字を配列に入れなおす必要はない。
- for を使って、先頭から1文字ずつ取り出していく方法が最も効率的。
fn main() { let 文字列 = "あいうえおかきくけこさしすせそ"; for 文字 in 文字列.chars() { println!("{}", 文字); } }
Rust スレッドに終了命令を送る try_recv
概要
- スレッドに終了命令を送るには channel を使う。
- 受信の有無を確認するには try_recv を使う。
- 受信内容を確認するには match を使う。
プログラム例
- 本スレッドで1秒待機している間に、別スレッドはカウンタを回す。
- 本スレッドは別スレッドを停止させて、カウンタを表示する。
use std::sync::mpsc::channel; use std::thread::{sleep, spawn}; use std::time::Duration; fn main() { let (送信機, 受信機) = channel(); let 別スレッド = spawn(move || { let mut カウンタ = 0 as i64; loop { match 受信機.try_recv() { Ok("停止") => break, _ => (), } カウンタ += 1; } カウンタ }); sleep(Duration::from_secs(1)); // 1秒停止 送信機.send("停止").unwrap_or(()); // 停止命令 let 結果 = 別スレッド.join().unwrap(); // 結果を取り出す println!("{}", 結果); }
結果
- 1秒間にカウンタを回せる数なので、環境によって差がある。
57502901
解説
fn main() { let (送信機, 受信機) = channel(); let 別スレッド = spawn(move || { let mut カウンタ: i64 = 0; // └ i32だと桁が足りなくなる可能性を考えてi64にした。 loop { match 受信機.try_recv() { // └ 受信していればOk(~)、なければErr(~)が得られる。 Ok("停止") => break, // └ 停止命令を受けたら脱出する。 _ => (), // └ それ以外なら何もしない。 } カウンタ += 1; } カウンタ // └ 別スレッドの結果は「カウンタ」になる。 }); sleep(Duration::from_secs(1)); // 1秒停止 送信機.send("停止").unwrap_or(()); // 停止命令 let 結果 = 別スレッド.join().unwrap(); // 結果を取り出す println!("{}", 結果); }
補足
- 既に終了しているスレッドに対して send で送信すると、Err が返ってくるため、unwrap() ではプログラムが停止してしまう。
- 今回のプログラムは、unwrap() の代わりに unwrap_or(()) を書いて、失敗時にそのまま処理を継続するようにした。
Rust スレッド間の通信 channel, send, recv
概要
- 別のスレッドにデータを送受信するには channel を使う。
- データを送信するには send を使う。
- データを受信するには recv を使う。
プログラム例
- 1から50までの和をスレッド1で計算する。
- 51から100までの和をスレッド2で計算する。
- 本スレッドでそれらの合計を表示する。
use std::thread::{spawn}; use std::sync::mpsc::{channel}; fn main() { let (送信機1, 受信機) = channel(); let 送信機2 = 送信機1.clone(); spawn(move || { 送信機1.send(合計を求める(1, 50)).unwrap(); }); spawn(move || { 送信機2.send(合計を求める(51, 100)).unwrap(); }); let 結果1 = 受信機.recv().unwrap(); let 結果2 = 受信機.recv().unwrap(); println!("{}", 結果1 + 結果2); } fn 合計を求める(開始: i32, 終了: i32) -> i32 { (開始..=終了).fold(0, |sum, i| sum + i) }
解説
fn main() { let (送信機1, 受信機) = channel(); // └ スレッド間の通信に使える送信機と受信機のセットが得られる。 let 送信機2 = 送信機1.clone(); // └ 送信機はコピーできる。 spawn(move || { // └ move ◁「受信機1」をスレッド内で使えるようにする。 送信機1.send(合計を求める(1, 50)).unwrap(); // └ スレッド1からは、1から50の合計を送信する(エラー処理を省略)。 }); spawn(move || { 送信機2.send(合計を求める(51, 100)).unwrap(); // └ スレッド2からは、51から100の合計を送信する(エラー処理を省略)。 }); let 結果1 = 受信機.recv().unwrap(); let 結果2 = 受信機.recv().unwrap(); // └ 2回受信するまで待ち続ける(エラー処理を省略)。 println!("{}", 結果1 + 結果2); }
補足
- join() でデータを受け取れるのは一回きりだが、recv() では何回でも受け取れる。
- send() でエラーになるのは、相手のスレッドがすでに終了している場合。
- recv() でエラーになるのは、受信機に対応する送信機がすべて破棄されていた場合。
- 受信機が生きている間は、recv() すると無限待ち状態になるので注意。
- 受信に時間制限を設けるには recv_timeout(Duration::from_secs(1)) のようにする。
- タイムアウト時はエラーが発生するので match で処理する。
Rust スレッドの生成と合流 spawn, join
概要
- 複数の処理を並行して実行するにはスレッドを生成する。
- スレッドを生成するには「spawn」を使う。
- スレッドの終了を待つには「join」を使う。
- 処理をある時間だけ停止させるには「sleep」を使う。
プログラム例
- 別スレッドで0.5秒間隔で5回「▲」と表示する。
- 本スレッドで0.3秒間隔で5回「○」と表示する。
use std::thread::{spawn, sleep}; use std::time::{Duration}; fn main() { let 別スレッド = spawn(|| { for i in 1..=5 { sleep(Duration::from_millis(500)); println!("▲ {:.1}", (i as f64) * 0.5); } }); for i in 1..=5 { sleep(Duration::from_millis(300)); println!("○ {:.1}", (i as f64) * 0.3); } 別スレッド.join().unwrap(); }
結果
- 1.5のところは、ほぼ同時に実行されているので、前後することがある。
○ 0.3 ▲ 0.5 ○ 0.6 ○ 0.9 ▲ 1.0 ○ 1.2 ▲ 1.5 ○ 1.5 ▲ 2.0 ▲ 2.5
解説
use std::thread::{spawn, sleep}; use std::time::{Duration}; fn main() { let 別スレッド = spawn(|| { // └ 以下のような処理のスレッドを生成し実行する。 for i in 1..=5 { sleep(Duration::from_millis(500)); // └ 500ミリ秒、停止する。 println!("▲ {:.1}", (i as f64) * 0.5); // └ {:.1} ◁小数点以下1桁表示する。 // └ i as f64 ◁整数を実数に変換する。実数同士は計算できる。 } }); for i in 1..=5 { sleep(Duration::from_millis(300)); println!("○ {:.1}", (i as f64) * 0.3); } 別スレッド.join().unwrap(); // └ 別スレッドが終了するまで待機する。 }
Rust 関数で処理に名前をつける fn
概要
- 関数を作ると、処理に名前をつけることができる。
- 関数を作成するには「fn」を使う。
- 入れる値と出る値の種類(型)を指定する必要がある。
プログラム例
- 関数の名前は「ワンワン」とする。
- 関数に数値(i32)を渡すと、文字列(String)が出てくる。
- 渡した数値の数だけ「ワン!」を繰り返す。
fn main() { println!("{}", ワンワン(10)); // 数値を変えてもよい } fn ワンワン(回数: i32) -> String { let mut 文字列 = String::new(); for _ in 1..=回数 { 文字列 += "ワン!"; } 文字列 }
解説
- 関数「ワンワン」に 3 を与えると、文字列「ワン!ワン!ワン!」が出てくる。
- 処理に名前を付けて関数を作ると、プログラムの意味が分かりやすくなる。
- それぞれの行の意味は以下の通り。
fn ワンワン(回数: i32) -> String { // └ fn ワンワン ◁関数を作る。 // └ 回数: i32 ◁変数「回数」を入力として受け取る。型は i32(整数)。 // └ -> String ◁この関数は String(文字列)を出力する。 let mut 文字列 = String::new(); // └ let mut ◁編集可能な変数を作る。 // └ String::new() ◁空っぽの文字列を変数に入れる。 for _ in 1..=回数 { // └ for ◁繰り返し処理 // └ _ in 1..=回数 ◁1~回数の間、繰り返す。 文字列 += "ワン!"; // └ 文字列に「ワン!」を連結する。 } 文字列 // └ セミコロン(;)がないので、この関数の出力は「文字列」となる。 }
変数の型
- 変数にはいくつかの種類がある。
fn 関数(引数: i32) -> String { } // └ ここでいう i32 や String が変数の種類を表す。「型」という。
いろいろな型(抜粋)
型の名前 | 保持できる値 | 使いかた |
---|---|---|
i8 | 整数 -128~127 | let 変数: i8 = -99; |
i16 | 整数 -32,768~32,767 | let 変数: i16 = -999; |
i32 | 整数 -2,147,483,648~2,147,483,647 | let 変数: i32 = -99999; |
i64 | 整数 約-9.2×1018~9.2×1018 | let 変数: i64 = -9999999999; |
i128 | 整数 約-1.7×1038~1.7×1038 | let 変数: i128 = -999999999999999999; |
u32 | 正の整数 0~4,294,967,295 | let 変数: i32 = 99999; |
f64 | 実数 約-1.7×10308~1.7×10308 | let 変数: f64 = 3.14; |
bool | true(真=1)・false(偽=0) | let 変数名: bool = true; |
String | 文字列(長さはいくらでも) | let 変数名: String = "あいうえお"; |
Rust 分岐についてもっと詳しく if, else
概要
- 「if」「else」でプログラムの流れを分岐できる。
- 「match」よりも単純な分岐に向いている。
プログラム例
- 持ち金が5000円以上なら「n円も持っている」と表示する。
- 持ち金が5000円より少なければ「n円しか持っていない」と表示する。
fn main() { let 持ち金 = 4000; // 数値を変えてよい if 持ち金 >= 5000 { println!("{}円も持っている", 持ち金); } else { println!("{}円しか持っていない", 持ち金); } }
解説
- 「if 変数 >= 5000 { ~ }」 で、変数が5000かそれより大なら~する、という意味になる。
- 当てはまらなければ「else { ~ }」が実行される。
- 「else { ~ } 」は無くても問題ない。
- 比較式の書き方は以下の通り。
比較式の書き方 | 意味 | 数学的にいえば |
---|---|---|
ア>イ | アがイより大きい | ア>イ |
ア>=イ | アがイより大きいか同じ | ア≧イ |
ア<イ | アがイより小さい | ア<イ |
ア<=イ | アがイより小さいか同じ | ア≦イ |
ア==イ | アがイと等しい | ア=イ |
ア!=イ | アがイと異なる | ア≠イ |
プログラム例2
- 「match」で同じことは表現できる。
- この場合は「if」「else」を使ったほうが短く書ける。
- シンプルに書けるほうを選択すればよい。
fn main() { let 持ち金 = 7000; // 数値を変えてよい match 持ち金 { 数 if 数 >= 5000 => println!("{}円も持っている", 持ち金), _ => println!("{}円しか持っていない", 持ち金) } }
Rust 練習問題「3がつくとアホになる」
概要
- 2010年ごろに流行した「世界のナベアツ」のネタを Rust で再現する。
- 分岐の「match」や「if」、繰り返しの「for」を使えばできるはず。
- 以下の仕様を満たすようなプログラムを Rust で組んでみよう。
仕様
- 1 からカウントアップしていく。「1」「2」
- 3 の倍数のときアホになる。「3🤪」...「11」「12🤪」
- 数字に 3 が含まれるときもアホになる。「13🤪」
- 100 になったら「オモロー」と言って終わる。「100😲オモロー!」
プログラム例
- 処理ごとに関数に分割した。
- 記述は長いが、分かりやすい。
fn main() { for i in 1..=99 { ナベアツ処理(i); } オモロー(); } fn ナベアツ処理(n: i32) { match 三の倍数か(n) || 三を含むか(n) { true => アホになる(n), false => 普通に言う(n) }; } fn 三の倍数か(n: i32) -> bool { n % 3 == 0 } fn 三を含むか(n: i32) -> bool { 一の位が3か(n) || 十の位が3か(n) } fn 一の位が3か(n: i32) -> bool { n / 10 % 10 == 3 } fn 十の位が3か(n: i32) -> bool { n % 10 == 3 } fn アホになる(n: i32) { println!("{}🤪", n) } fn 普通に言う(n: i32) { println!("{}", n) } fn オモロー(){ println!("100😲オモロー!"); }
プログラム例
- 同じ処理を、より短く表現した。
- 暗号めいている。
fn main() { for i in 1..=99 { let s = match i / 10 % 10 == 3 || i % 10 == 3 || i % 3 == 0 { true => "🤪", false => "" }; println!("{}{}", i, s); } println!("100😲オモロー!"); }
Rust 繰り返しループ for, loop, break
概要
- 処理の繰り返し(ループ)をするには for を使う。
- 繰り返し回数が読めないときは loop を使う。
プログラム例
- 1 から 10000 までの数の合計を表示する。
fn main() { let mut 合計 = 0; // 後で変更されるので mut が必要 for 回数 in 1..=10000 { 合計 += 回数; } println!("{}", 合計); }
解説
- 「for 変数 in 1..=10000」で、1~10000 の繰り返しができる。
- 繰り返しカウンタは、変数に格納される。
プログラム例2
- さっきと同じで、1 から 10000 までの数の合計を表示する。
fn main() { let mut 合計 = 0; let mut 回数 = 1; loop { 合計 += 回数; match 回数 { 10000 => break, // 10000なら脱出 _ => 回数 += 1 // そうでなければカウンタを増やす } } println!("{}", 合計); }
解説
- 「loop」のブロック内は無限に実行され続ける。
- 無限ループだと困るので、回数が10000の時点で分岐して脱出した。
- 記述が長くなるので、普段は「for」を使っておけばいい。
Rust 数値で分岐する match, ..=, |
概要
- 数値で分岐するには match を使う。
- 数値の範囲で処理を分けたい場合は「..=」を使う。
- いくつかの数値の場合に対応する場合は「|」を使う。
プログラム例
- 文字列の入力を要求する。
- 0~59 なら「不合格」を表示する。
- 60~99 なら「合格」を表示する。
- 100 なら「満点」を表示する。
- それ以外なら「範囲外」を表示する。
use std::io::*; fn main() { let 入力 = コンソール入力(); let 数値 = 入力.parse::<i32>().unwrap(); // 変換エラー対策は省略 let 出力 = match 数値 { 0..=59 => "不合格", 60..=99 => "合格", 100 => "満点", _ => "範囲外" }; println!("{}", 出力); } fn コンソール入力() -> String { let mut バッファ = String::new(); stdin().read_line(&mut バッファ).unwrap(); バッファ.trim().to_string() }
解説
- 「0..=100」は、0から100まで(100を含む)の範囲を表す。
プログラム例2
- 文字列の入力を要求する。
- 1, 3, 5, 7, 8, 10, 12 なら「31日」を表示する。
- 4, 6, 9, 11 なら「30日」を表示する。
- 2 なら「28日」を表示する。
- それ以外なら「範囲外」を表示する。
use std::io::*; fn main() { let 入力 = コンソール入力(); let 数値 = 入力.parse::<i32>().unwrap(); // 変換エラー対策は省略 let 出力 = match 数値 { 1 | 3 | 5 | 7 | 8 | 10 | 12 => "31日", 4 | 6 | 9 | 11 => "30日", 2 => "28日", _ => "範囲外" }; println!("{}", 出力); } fn コンソール入力() -> String { let mut バッファ = String::new(); stdin().read_line(&mut バッファ).unwrap(); バッファ.trim().to_string() }
Rust 文字列で分岐する match, as_str
概要
- 分岐するには match を使う。
- 文字列を比較対象にするには as_str を使う。
コード
- 入力されたのが「いぬ」なら「ワン」と出力する。
- 入力されたのが「ねこ」なら「ニャー」と出力する。
- 入力されたのが「ひと」なら「オギャア」と出力する。
- それ以外が入力されたら「?」と出力する。
use std::io::*; fn main() { let 入力 = コンソール入力(); let 出力 = match 入力.as_str() { "いぬ" => "ワン", "ねこ" => "ニャー", "ひと" => "オギャア", _ => "?" }; println!("{}", 出力); } fn コンソール入力() -> String { let mut バッファ = String::new(); stdin().read_line(&mut バッファ).unwrap(); バッファ.trim().to_string() }
解説
fn main() { let 入力 = コンソール入力(); // └ コーンソール入力された文字列を「入力」に入れる。 let 出力 = match 入力.as_str() { // └ 入力.as_str:伸ばせる文字列を、固定文字列に変換する。 // └ match:固定文字列の内容で場合分けする。 "いぬ" => "ワン", "ねこ" => "ニャー", "ひと" => "オギャア", // └ "いぬ" に一致すれば "ワン" を「出力」に入れる。 _ => "?" // └ 一致しなかった場合 "?" を「出力」に入れる。 }; println!("{}", 出力); }
Rust 入力した文字列を数値に変換する parse, match
概要
- 文字列を数値に変換するには parse を使う。
- 単純な実装だと、変換失敗時にプログラムが落ちる。
- 変換失敗時の処理を書くには match を使う。
コード
- 文字列の入力を要求し、99 を掛け算する。
use std::io::*; fn main() { let 入力 = コンソール入力(); let 数値: i32 = 入力.parse().unwrap(); println!("{} × 99 = {}", 数値, 数値 * 99); } fn コンソール入力() -> String { let mut バッファ = String::new(); stdin().read_line(&mut バッファ).unwrap(); バッファ.trim().to_string() }
- 7ケタ程度の整数を入力した場合は成功する。
- ケタの大きすぎる整数や、数値以外の入力をするとプログラムが落ちてしまう。
改善
- 数値以外の入力をされたら「無効な値」と表示して終了する。
use std::io::*; fn main() { let 入力 = コンソール入力(); let 数値 = match 入力.parse::<i32>() { Ok(n) => n, // 成功時 Err(_) => { // 失敗時 println!("{}", "無効な値"); return } }; println!("{} × 99 = {}", 数値, 数値 * 99); } fn コンソール入力() -> String { let mut バッファ = String::new(); stdin().read_line(&mut バッファ).unwrap(); バッファ.trim().to_string() }
解説
- 各行の意味は以下の通り。
fn main() { let 入力 = コンソール入力(); // └ 変数「入力」にはコンソール入力した文字列が入る。 let 数値 = match 入力.parse::<i32>() { // └ 入力.parse::<i32>():変数「入力」を i32(整数)に変換する。 // └ match:変換の結果によって処理が分岐する。 Ok(n) => n, // └ 変換に成功したら、値を取り出して「数値」に入れる。 Err(_) => { // └ 変換に失敗した場合の処理が続く。 println!("{}", "無効な値"); return // └ return:関数「main」から脱出する。以下は処理されない。 } }; println!("{} × 99 = {}", 数値, 数値 * 99); }
- 文字列.parse::<i32>()で、文字列を整数に変換する。
- i32 は、二進法の32ケタで表せる整数を表す。-2,147,483,648 ~ +2,147,483,647
- 桁数の異なるいくつかの変数の種類がある。
- i8(二進法 8 ケタ:-128 ~ +127)
- i16(二進法 16 ケタ:-32,768 ~ 32,767)
- i64(二進法 64 ケタ:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)