024号文書

主にプログラミング

PythonistaがRustはじめました#003 -- 標準出力・max関数

昨日はM-SOLUTIONSプロコンおつかれさまでした。 PythonでModIntするたびに制限時間大丈夫か不安を感じながらSubmitしちゃうので、こういうのは確実にRustで通せるerになりたいですね。 そして本日はAGC! 2完しないといけないプレッシャーに負けず、3完目指して頑張るぞい。

前回の記事で、main関数と標準出力について、説明するのを完全に忘れていたので、補足します。 その後、ABC098-A を解くために、vec型のmax関数を使った話を書きます。

main関数

競技プログラミングのおいて、C++同様にmain関数の呼び出しからプログラムはスタートします。 したがって、任意の問題について必ず書くコードになります。 main関数の書き方は以下のとおりです。

fn main() {
    // 処理を書く
}

returnは書く必要がないようです。

標準出力

標準出力をしない問題はおそらくないでしょう。 Rustでは println! マクロを使います。 フォーマット文字列の標準出力も可能です。 単一の値ansを標準出力する例は以下です。

println!("{}", ans)

複数の値を出力する場合や浮動小数点数を桁数いい感じにして出力するケースについてはいずれ説明します(まだ分かっていない)。

ABC098-A

入出力の概要は以下の通りです。

  • 入力: 整数A, B
  • 出力: max(A + B, A - B, AB)

元の問題: https://atcoder.jp/contests/abc098/tasks/abc098_a

max関数

さて、四則演算をマスターしている私にとっては、maxするだけの問題です。 そこで、 Rust max でググってみます。 それっぽいのがヒットしました。

https://doc.rust-lang.org/std/cmp/fn.max.html

よしよし、importっぽいのをしてはい終わり...と思いきや、よく見たら引数が二つに限定されているではありませんか。 さすがにn引数のmaxはどっかにあるやろ〜wと思いググるを続行しますが見つからず。 諦めて、 max(A + B, max(A - B, A * B)) のように書く手もありますが、今後の人生を考えると任意の個数の値に対するmaxをとれるようにならないと辛いのは間違いないです。 ...ん、任意の個数といえばリストにmaxメソッドはないのだろうか? 少し調べてみると、PythonでいうリストはRustでいうVecのようです。 そして、Vecから変換可能なIterator型には、案の定maxをはじめした美味しそうな関数がたくさんありました!

https://doc.rust-lang.org/std/iter/trait.Iterator.html

したがって、以下の流れで解を得られそうです。

  1. getマクロ(前回導入したマクロ)を使ってA, Bを入力する
  2. (A + B, A - B, A * B) を要素とするVecを作成する
  3. 作成したVecをIteratorに変換する
  4. 変換したIteratorオブジェクトのmaxメソッドを呼び出す

しかし、注意点が二つありました。

注意点1: IterのmaxメソッドはOption型である

PythonのtypesやJavaを使ったことがある人なら推測できるかと思いますが、Optionというのは空値かもしれないことを表すデータ型です。 冷静に考えれば、戻り値の型としては理にかなっていますね。 Vecが空の場合にmaxが定義できないことをケアしているのです。 とはいえ今回の問題ではVecが空でないことは約束されているので、乱暴に値を取り出してしまいましょう。 値を取り出すにはunwrapというメソッドを使えばよさそうです。

https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap

注意点2: Iter型ではなくIntoIter型に変換する必要がある

注意点1を踏まえて、以下のコードを書きました。

fn main() {
    // 入力
    let (A, B) = get!(i64, i64);

    let ans = vec![A + B, A - B, A * B].iter().max().unwrap();

    // 出力
    println!("{}", ans);
}

すると、以下のコンパイルエラーが吐かれてしまいました...

$ rustc a.rs -o a.out
error[E0597]: borrowed value does not live long enough
  --> a.rs:47:15
   |
47 |     let ans = vec![A + B, A - B, A * B].iter().max().unwrap();
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^                      - temporary value dropped here while still borrowed
   |               |
   |               temporary value does not live long enough
...
51 | }
   | - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.

どうもRustで難しいと言われている所有権とかその辺の問題っぽいです。 所有権の理解は今後の課題として、お手軽にmaxを取る方法を調べると、どうやらIteratorではなくIntoIteratorにすればよいようです(Iteratorはオリジナルに対して走査するのに対して、IntoIteratorはクローンを得るイメージなのかな?)。

したがって、コードを以下の通り変更します。

fn main() {
    // 入力
    let (A, B) = get!(i64, i64);

    let ans = vec![A + B, A - B, A * B].into_iter().max().unwrap();

    // 出力
    println!("{}", ans);
}

無事、通りました! https://atcoder.jp/contests/abc098/submissions/5694071

次回はループを使った問題に挑戦したお話を書く予定です。 さて、AGC頑張るぞい!