競技プログラミングで、↓ みたいな形の経路を生成したくなることはありませんでしょうか。
─────┐ ┌────┘ └────┐ ┌────┘ └─────
みなさま「あります。」
出題例「これです。 https://atcoder.jp/contests/abc069/tasks/arc080_b 」
方法 1(推奨): Either を使う(外部ライブラリ)
Either はなんと、Iterator を実装しています。便利ですね。
このライブラリ他にも細かいトレイトやメソッドが豊富に整備されていて、至れり尽くせり、正直なめていました。
for (i, j) in (0..h).flat_map(|i| { match i % 2 { 0 => Either::Left(0..w), 1 => Either::Right((0..w).rev()), _ => unreachable!(), } .map(move |j| (i, j)) }) { println!("{i} {j}"); }
方法 2: 変数変換をする
競プロっぽくて(?)よいという説もあります。とはいえ個人的には
- やりたいことそのものをそのまま書いた感じではないので読みにくいですし、間違いの温床でしょう
- 今回偶々長さも同じですし、単純な変換をするだけで済みましたけれども、一般のイテレータではそうはいきませんよね
という理由で、ドストレートではないかなという気持ちです。
for (i, j) in (0..h).flat_map(|i| { (0..w).map(move |j| match i % 2 { 0 => (i, j), 1 => (i, w - 1 - j), _ => unreachable!(), }) }) { println!("{i} {j}"); }
方法 3: Box を使う
当然余計なメモリ確保は走りますが、見た目もシンタックスがゴツいことを除けばやりたいことそのままって感じですし、案外悪くないでしょうという気持ちはあります。
for (i, j) in (0..h).flat_map(|i| { let js: Box<dyn Iterator<Item = usize>> = match i % 2 { 0 => Box::new(0..w), 1 => Box::new((0..w).rev()), _ => unreachable!(), }; js.map(move |j| (i, j)) }) { println!("{i} {j}"); }
どちらの方がマシかわかりませんが、最初のアームに as を書くという方法もあります。
for (i, j) in (0..h).flat_map(|i| { match i % 2 { 0 => Box::new(0..w) as Box<dyn Iterator<Item = usize>>, 1 => Box::new((0..w).rev()), _ => unreachable!(), } .map(move |j| (i, j)) }) { println!("{i} {j}"); }