久々の第29回です。前回はこちら。
[第29回の様子]
2022/03/02に第29回を開催した。
前回が2022/02/09だったので、実に3週間ぶり。自分が体調を崩したり、祝日だったりで間が空いてしまった。
内容としてはRust By Example 日本語版の「9.3. 高階関数」に取り組んだ。
参加者は5人。久しぶりなのにたくさん集まってくれて嬉しい😭
[学んだこと]
- 9.3. 高階関数
- Rustは高階関数を扱えますよ、と。まあもう既に散々クロージャとか触ってきたわけで今更感のある説明...
- 今回のお題は「1000以下の奇数を2乗した値の合計を求める」というもの
- これを宣言型プログラミングで解くとこうなる
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
let upper = 1000;
let mut acc = 0;
for n in 0.. {
let n_squared = n * n; // nを2乗した値
if n_squared >= upper { // 1000を超えたら終わり
break;
} else if is_odd(n_squared) { // 奇数だったら足す
acc += n_squared;
}
}
println!("imperative style: {}", acc); // 5456
- 新しい概念として、0から無限までイテレートする方法が紹介されていた。
loopと違って数を利用したい場合はこっちが便利そう。
for n in 0.. {
// 0から無限まで
}
// これまでに学んだRustの無限ループ
loop {
// do something
}
- さて、お題に戻る
- これを高階関数を利用して、関数型のアプローチをすると以下のようになる
fn is_odd(n: u32) -> bool {
n % 2 == 1
}
let upper = 1000;
// ここまでは宣言型と共通
let sum_of_squared_odd_numbers: u32 =
(0..) // 0から無限までの自然数
.map(|n| n * n) // 2乗を求めて
.take_while(|&n_squared| n_squared < upper) // 上限値1000未満のもの
.filter(|&n_squared| is_odd(n_squared)) // 奇数のもの
.fold(0, |acc, n_squared| acc + n_squared); // 足し合わせる
println!("functional style: {}", sum_of_squared_odd_numbers); // 5456
take_while()というのに馴染みがなかったが、条件を満たす間処理を継続する、条件を満たさないものがあったらそこで処理を止める、という関数らしい。なるほど。take_while()とfilter()は大体同じか?と思ったが、そうではなかった- filterの場合は全てを処理するので、今回であれば無限まで処理をするため、下記のようにオーバーフローが発生する
let sum_of_squared_odd_numbers: u32 =
(0..)
.map(|n| n * n)
.filter(|&n_squared| n_squared < upper) // 本当はtake_while
.filter(|&n_squared| is_odd(n_squared))
.fold(0, |acc, n_squared| acc + n_squared);
thread 'main' panicked at 'attempt to multiply with overflow', src/main.rs:37:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
- ちなみに、今回の処理であれば
take_while()とfilter()の処理の順番を変えても結果は同じだった。それはそうか...
let sum_of_squared_odd_numbers: u32 =
(0..) // 0から無限までの自然数
.map(|n| n * n) // 2乗を求めて
.filter(|&n_squared| is_odd(n_squared)) // 奇数のもの
.take_while(|&n_squared| n_squared < upper) // 上限値1000未満のもの
.fold(0, |acc, n_squared| acc + n_squared); // 足し合わせる
println!("functional style: {}", sum_of_squared_odd_numbers); // 5456
- ちょっと時間が余ったので、他にもいろんなイテレータがあるということで下記の記事を紹介してもらった
- イテレータを永遠にループさせるcycle()とかは、何に使うのか正直よくわからなかったが、「テトリスとかジャンケンとか決まった要素を順番に取り出すのに使う」と説明してもらってなんとなく分かった気がした。ゲームだとランダム性が必要な気もするけどそれは他と組み合わせればなんとかなるか。
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
今週は久しぶりだったこともあって、軽めに済ませた。と言っても、無限までイテレートする新しい手法も学んだし、満足。
今週のプルリクエストはこちら。