ifは上へ、forは下へ ― 分岐とループの置き場所
分岐は呼び出し側へ、ループはまとめて処理する関数の中へ。Rustの小技を、DBのクエリ最適化と同じ発想として整理し、使えない場面まで示した記事。
#なぜ今日これを選んだか
「ifは上に、forは下に」は TigerBeetle の Tiger Style や matklad が勧める書き方で、Rust のコードでよく見かけます。この記事はそれを「なんとなくの好み」で終わらせず、データベースのクエリ最適化と同じ理屈だと示し、さらに使えない場面まで書いています。小技の理由と限界が分かると、自分で判断できるようになります。
#要点
frobnicate(Option<Walrus>)のように中でNoneを判定する関数をやめる。呼び出し側でNoneを捨てて、Vec<Walrus>を受け取るfrobnicate_batchに渡す。分岐は境界へ、ループはまとめて処理する側へ寄せる- これはDBが条件での絞り込みをテーブルの読み込みの近くまで下ろし、結合を後回しにし、行をまとめて処理するのと同じ発想。
filter p . map f == map f . filter (p . f)という法則で裏付けている - 限界もはっきりしている。ループの外へ出せる
ifは条件がループ中で変わらないものだけ。先に絞って得をするのは、判定が安く済む時だけ。まとめて処理するのは「速いかどうか」で決めることで、同じ動きを保つための書き換えではない
#自分にとっての示唆
可動域アプリでは、フレームごとに「関節が写っているか」を確かめてから角度を計算しています。この判定を計算の関数の中に置くのではなく、入口で写っていないフレームを捨て、残りをまとめて計算に渡す形にすると、関数は分岐のない素直な形になり、テストもしやすくなります。ただし記事が言う通り、判定が重いなら順番を変えても得はありません。明日は計算部分の関数を1つ選び、Option を受け取っていないかを見て、受け取っていれば呼び出し側へ分岐を移す小さな書き換えを1コミットだけやってみます。Rust を学ぶうえでも「型で状態を表す」練習になります。