Rustで競技プログラミング!標準入力の受け取り方を解説

Rustで競技プログラミング!標準入力の受け取り方を解説

こんにちは!最近、Rustで競技プログラミングに挑戦しようと奮闘中です。

プログラミングの学習において、競技プログラミングは非常に良いトレーニングになります。
「この後何をすればいいの?」という、学習の停滞期を打破するきっかけにもなるでしょう。

しかし、Rustで競技プログラミングを始めようとした時に、最初にぶつかる壁が…そう、標準入力の受け取りです!

「他の言語では簡単にできるのに、Rustだとどう書けばいいの…?」
「呪文みたいなコードが出てきたけど、これ何…?」

今回は、そんな悩みを解決すべく、Rustで競技プログラミングの入力を受け取るための、基本的なコードとその解説をまとめました。
私自身の学習メモも兼ねていますので、Rust初心者の方も、一緒に標準入力の壁を乗り越えましょう!

Rustで競技プログラミング:まずは標準入力を理解しよう

競技プログラミングでは、問題のデータが標準入力から与えられることがほとんどです。
例えば、AtCoder等で出題される問題では、

1 2

のように、スペース区切りの数値が入力されます。

この入力を受け取り、プログラムで処理できるようにするには、どうすれば良いのでしょうか?

Rustで標準入力を受け取るためのコードは、一見すると複雑に見えるかもしれません。
しかし、一つずつ分解して理解すれば、決して難しくありません!

Rustで標準入力を受け取るコード例 (スペース区切りで2つの整数)

まずは、以下のコードを見てください。

fn main() {
    let (a, b) = {
        let mut s = String::new();
        std::io::stdin()
            .read_line(&mut s)
            .unwrap();
        let mut iter = s.split_whitespace()
            .map(|i| i.parse::()
            .unwrap());
        (iter.next().unwrap(), iter.next().unwrap())
    };

    println!("{}", a + b);
}

このコードは、スペース区切りで2つの整数を受け取り、その合計を出力する、という処理を行います。

…たったこれだけのコードなのに、見慣れない記号やメソッドがたくさん出てきて、混乱してしまいますよね?
私も最初はそうでした!

でも、大丈夫!一つずつ丁寧に解説していきます。

(今回はこのコードについてAIに解説してもらいました。)

AI:

このコードは、大きく分けて以下の2つの部分から構成されています。

  1. 標準入力から2つの整数を受け取る部分
  2. 受け取った2つの整数の和を出力する部分

1. 標準入力から2つの整数を受け取る部分


let (a, b) = {
    let mut s = String::new();
    std::io::stdin()
        .read_line(&mut s)
        .unwrap();
    let mut iter = s.split_whitespace()
        .map(|i| i.parse::<i32>()
        .unwrap());
    (iter.next().unwrap(), iter.next().unwrap())
};

この部分をさらに細かく分解して解説します。


let (a, b) = { ... };

これは、Rustの「パターンマッチ」と「ブロック」を組み合わせた構文です。
{ … } の中の処理結果(この場合はタプル)を、let (a, b) というパターンに束縛(代入)しています。
つまり、{ … } の中で計算された2つの値が、それぞれ変数 a と b に格納されます。


let mut s = String::new();
  • String::new() は、新しい空の文字列 (String 型) を作成します。
  • let mut s は、s という名前の変数を宣言し、その変数を「変更可能 (mutable)」にします。
  • Rustでは、変数はデフォルトで変更不可能 (immutable) です。
  • mut キーワードをつけることで、後から値を変更できるようになります。

この s は、標準入力から読み込んだ文字列を格納するために使われます。


std::io::stdin()
  • これは、標準入力を表すオブジェクトを取得します。
  • std::io は、標準入出力に関する機能を提供するモジュールです。
  • stdin() 関数は、標準入力からの入力を扱うための Stdin 構造体のインスタンスを返します。

.read_line(&mut s)
  • これは、Stdin 構造体のメソッド read_line を呼び出しています。
  • read_line メソッドは、標準入力から1行分の文字列を読み込み、それを引数で指定された文字列 (s) に格納します。
  • &mut s は、「s への変更可能な参照」を渡しています。
  • read_line は、読み込んだ文字列を s に書き込む必要があるため、変更可能な参照を渡す必要があります。
  • このメソッドは Result 型を返します。

.unwrap()
  • read_line メソッドは Result 型の値を返します。Result は、処理が成功したか失敗したかを表す型です。
    • 成功した場合: Ok(読み込んだバイト数)
    • 失敗した場合: Err(エラー情報)
  • .unwrap() は、ResultOk の場合はその中身(この場合は読み込んだバイト数)を取り出し、Err の場合はプログラムを強制終了(パニック)させます。
  • 競技プログラミングでは、入力形式が保証されていることが多いため、エラー処理を省略して unwrap() を使うことがよくあります。
  • しかし、本来はエラーハンドリングを適切に行うのが望ましいです。
  • ここでは、read_lineの結果は使用しないため、Resultの中身は捨てられます

let mut iter = s.split_whitespace()...;
  • s.split_whitespace() は、文字列 s を空白文字(スペース、タブ、改行など)で区切って、イテレータ (iterator) を作成します。
  • イテレータは、要素を順番に取り出すことができるオブジェクトです。
  • split_whitespace() は、文字列を分割するたびに &str (文字列スライス) を生成するイテレータを返します。
  • let mut iter は、このイテレータを iter という名前の変数に束縛しています。
  • ここでも mut を付けているのは、イテレータから要素を取り出すたびにイテレータの状態が変化するためです。

.map(|i| i.parse::<i32>().unwrap())
</i32>
  • .map(...) は、イテレータの各要素に対して、指定された処理を適用するメソッドです。
  • |i| ... は、「クロージャ」と呼ばれる無名関数です。
  • i は、イテレータから取り出された各要素(この場合は &str)を表す仮引数です。
  • ... の部分で、i に対する処理を記述します。
  • i.parse::<i32>() は、文字列スライス ii32 型の整数に変換しようと試みます。
  • parse() メソッドも Result 型を返します。
    • 成功した場合: Ok(変換された整数)
    • 失敗した場合: Err(エラー情報)
  • .unwrap() は、parse() の結果が Ok の場合はその中身(変換された整数)を取り出し、Err の場合はプログラムをパニックさせます。

(iter.next().unwrap(), iter.next().unwrap())
  • iter.next() は、イテレータから次の要素を取り出します。
    • 次の要素がある場合: Some(要素)
    • 次の要素がない場合: None
  • .unwrap() は、next() の結果が Some の場合はその中身を取り出し、None の場合はプログラムをパニックさせます。
  • ここでは、入力が必ず2つの整数を含むことが前提となっているため、unwrap() を使っています。
  • (..., ...) は、2つの値からなるタプルを作成します。
  • ここでは、イテレータから取り出した2つの整数をタプルにまとめています。

2. 受け取った2つの整数の和を出力する部分


println!("{}", a + b);
  • println!(...) は、指定された文字列を標準出力に出力するマクロです。
  • "{}" は、プレースホルダーと呼ばれるもので、後続の引数の値で置き換えられます。
  • a + b は、変数 ab に格納された整数の和を計算します。

最終的に、計算された和が標準出力に出力されます。

はい!AI先生、今回も丁寧な解説ありがとうございます!
おかげで、あの呪文のようなコードが、何をしているのか理解できました。(たぶん)

まとめ:Rustでの競技プログラミング、まずは入力から!

今回は、Rustで競技プログラミングに挑戦する上で避けて通れない、標準入力の受け取り方について解説しました。

一見複雑に見えるコードも、一つずつ分解して理解すれば、それほど難しくありません。
まずは、今回紹介したコードを「おまじない」として覚えてしまい、徐々にその意味を理解していく、という方法でも良いでしょう。

Rustには、まだまだたくさんの機能があります。
所有権、エラー処理、ジェネリクス、トレイト…などなど、学ぶべきことは山積みです。
でも、焦らず、一歩ずつ、着実に学習を進めていけば、きっとRustをマスターできるはず!
私も、競技プログラミングに挑戦しながら、Rustの理解を深めていきたいと思います。

Comments

No comments yet. Why don’t you start the discussion?

    コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です