024号文書

主にプログラミング

PythonistaがRustはじめました#002 -- 標準入力と四則演算

3日ぶりのRust記事です。 一昨日は、がブリチキンで美味しい唐揚げを堪能し、漬け込みハイボール6種を制覇していました。 その結果、一昨日と昨日は犠牲になったのだ...

今日はRustインストール後、ABC128-Aを通すまでのお話です。

ABC128-A の問題

端的に言うと、入出力が以下である問題です。

  • 入力: 非負整数 A, P
  • 出力: floor((3A + P) / 2)

元の問題: https://atcoder.jp/contests/abc128/tasks/abc128_a

標準入力

さて、最初の関門はどの言語でもお馴染みの標準入力です。 ざっと調べてみましたが、きちんと理解するにはミュータブルな変数の扱い、型の解釈、Optionalな値の扱いなどの知識が必要そうです。 全部を理解してから始めると挫折しそうなので、今回は先人のマクロをコピペするだけに留めます。 マクロの理解は今後少しずつ進めようと思います。

さて、コピペしたマクロは以下です。 なお、Qiita - Rustで競技プログラミング スターターキット を参考にさせていただきました。 このような優秀なコードを共有いただき、本当にありがとうございます。

macro_rules! get {
      ($t:ty) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              line.trim().parse::<$t>().unwrap()
          }
      };
      ($($t:ty),*) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              let mut iter = line.split_whitespace();
              (
                  $(iter.next().unwrap().parse::<$t>().unwrap(),)*
              )
          }
      };
      ($t:ty; $n:expr) => {
          (0..$n).map(|_|
              get!($t)
          ).collect::<Vec<_>>()
      };
      ($($t:ty),*; $n:expr) => {
          (0..$n).map(|_|
              get!($($t),*)
          ).collect::<Vec<_>>()
      };
      ($t:ty ;;) => {
          {
              let mut line: String = String::new();
              std::io::stdin().read_line(&mut line).unwrap();
              line.split_whitespace()
                  .map(|t| t.parse::<$t>().unwrap())
                  .collect::<Vec<_>>()
          }
      };
      ($t:ty ;; $n:expr) => {
          (0..$n).map(|_| get!($t ;;)).collect::<Vec<_>>()
      };
}

ABC128-A のように二つの整数を受け取るときは以下の通りサクッと書けます。

let (A, P) = get!(usize, usize);

気持ちいですね。 なお、型にusizeを指定していますが、基本的にはi64で宣言するのが安定かもしれません。 これについては、またいずれ述べます。

Rustの四則演算

さて、無事に入力は受け取ることができるようになりました。 ABC128-A を解くために必要な知識は四則演算だけですね。

四則演算に限らず、演算子と記号は The Rust Programming Launguage 付録B に まとめられています。 興味深い演算子もありますが、とりあえず四則演算に着目すると、C++Javaと変わらないようです。 なお、整数同士の除算はPythonとは異なり、デフォルトでfloorされるようです(参考: Trait Div)。

ABC128-A を解く

ABC128-A を解くための準備はすべて整いました! 早速コーディングしましょう(getマクロの定義は割愛します。)。

fn main() {
    // 入力
    let (A, P) = get!(usize, usize);

    let ans = (3 * A + P) / 2;

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

はい、シンプルですね! ちなみに、Rustのソースコードの拡張子はrsがメジャーっぽいです(https://doc.rust-jp.rs/book/second-edition/ch01-02-hello-world.html)。 AtCoderに提出するときは関係ないですけどね。

ローカルで実行するときは、忘れずにコンパイルします。コンパイルrustc コマンドでできます。 ↓の感じ。

$ rustc a.rs -o a.out
warning: variable `A` should have a snake case name
  --> a.rs:45:10
   |
45 |     let (A, P) = get!(usize, usize);
   |          ^ help: convert the identifier to snake case: `a`
   |
   = note: #[warn(non_snake_case)] on by default

warning: variable `P` should have a snake case name
  --> a.rs:45:13
   |
45 |     let (A, P) = get!(usize, usize);
   |             ^ help: convert the identifier to snake case: `p`

「大文字にしろ」という警告が出ていますが、無視します。 競技プログラミングでは問題文の通りに変数名を書きたくなるんですよねぇ。 コンパイル後は生成されたバイナリを実行するだけです。

$ ./a.out
1 3
3
$ ./a.out
0 1
0
$ ./a.out
32 21
58

サンプルは全てOKですね。 言語をPythonのままにしないように注意して提出。無事AC!! 提出結果は以下の通りです。

https://atcoder.jp/contests/abc128/submissions/5666954

上記の提出結果からも分かる通り、Qiita - Rustで競技プログラミング スターターキット によれば、実行時間に必ず2ms かかるみたいです。 まあだいたい制限時間は2000msなので、どうでもいいかな。

次回は、ABC098-Aを解く記事を書く予定です。 vecとmaxの使い方を習得していきます!