NF

地方で働くプログラマ

「並行プログラミング入門」読みすすめ その3(入力とか)

「並行プログラミング入門」読みすすめ その1 - NF
「並行プログラミング入門」読みすすめ その2 - NF
3回目、並行プログラミングからちょっと逸れて入力を学ぶ(問題解く用)
本の内容全然関係ないので注意してください。コンパイラはここを使います。
Online Rust Compiler - online editor

超基本

取り合えず教科書通りに書いてみる。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    println!("{}", s);
}

入力用の文字列型変数をmutで宣言して、read_lineにで更に&mutを指定して受け取る。
expect()は、関数(今回はread_line)が返すResult型がErrの時にプログラムをクラッシュさせて、指定して文字列を表示する。エラー出さずにクラッシュさせる場合は代わりにunwrap()で良いし、Okしか受け取らない場合はok()で良いらしい。
 

閑話休題

Okしか受け取らないって、実際にErr返ってきたらどうなるの?って思ったので試してみた。

fn foo() -> Result<i32, String> {
    Err("Error".to_string())
}
fn main() {
    foo().expect("Failed to read line");
    println!("{}", "test");
}

まずexpect()の場合、上記コードを実行すると標準エラーに以下が出力される。
expect()呼び出し時点でクラッシュするので、最後の"test"は標準出力されない。

thread 'main' panicked at 'Failed to read line: "Error"', main.rs:5:11

次に、「foo().expect(…)」を「foo().ok()」として実行すると、"test"が標準出力された。
と言う事で、Errは単にもみ消して次に進むっぽい。
 

指定の型で受け取る

本題に戻って、入力をもう少し掘り下げ。
入力値を整数型で受け取りたい場合は、こんな感じ。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    let i:i32 = s.trim().parse().ok().unwrap();
    println!("{}", i);
}

空白で区切られた複数の値を受け取って、配列ぽく格納(出力)する場合はこんな感じ。

use std::io;
fn main() {
    let mut s = String::new();
    io::stdin().read_line(&mut s).expect("Failed to read line");
    let v:Vec<i32> = s.trim().split_whitespace().map(|e| e.parse().ok().unwrap()).collect();
    
    for &item in v.iter() {
        print!("{}", item);
    }
}

trim()して空白を取り除いて…と長い、長くない?簡潔に書くやり方がありそうだが、一旦置いておく。
 

実演

ここまでの話の練習として、ABC286のA問題を解いてみる。入力はこんな感じで与えられる。

N
S1
S2
 ⋮
SN

入力だけやってみよう、と思ったら早速間違えました。

use std::io;
fn main() {
    let mut in_str = String::new();
    io::stdin().read_line(&mut in_str).ok();
    let N:i32 = in_str.trim().parse().ok().unwrap();
    print!("{}", N);
    
    for i in 0..N {
        io::stdin().read_line(&mut in_str).ok();
        print!("{}", in_str);
    }
}

これにサンプル2を入力したら、意図せず以下のように出力された。
read_lineの仕様見たら「appending it to the specified buffer.」と書いてあって、append(追加)なのね。

44
2023
4
2023
Year
4
2023
Year
New
4
2023
Year
New
Happy

あと、letで宣言したNを配列宣言の要素数に使おうとすると、constにして、というエラーになるんですが…
これは解決策が分からなかったので、今回は配列を使わないようにして、宿題にする。
 
気を取り直して、多分以下のコードで良さそう。

use std::io;
fn main() {
    let mut in_int = String::new();
    io::stdin().read_line(&mut in_int).ok();
    let N:i32 = in_int.trim().parse().ok().unwrap();

    let mut v:Vec<String> = vec!["".to_string(); 0];
    for i in 0..N {
        let mut in_str = String::new();
        io::stdin().read_line(&mut in_str).ok();
        v.push(in_str.trim().parse().ok().unwrap());
    }
    for item in v.iter().rev() {
        println!("{}", item);
    }
}

ポイントというかハマった所とかはこの辺。
・上で挙げた非const変数を配列数に指定できない
・Vec初期化の書き方が冗長な気がするけどもう少し何とかならないか(Vec::new()で良さそう)
・Vec型の変数(上のv)自体はiteratorじゃないのでrev()が使えない(先にv.reverse()しとくでも良さそう)
 

おまけ:proconio

他の方のACコード見てると、proconioというのがあるのに気付いた。(名前が直接的ですごい)
普通に使えるらしい。以下、まとめてくれている方の記事。
[Rustで簡単標準入力]proconio使い方まとめ - Qiita


ーーー
という訳で超初歩やりました。
全然慣れないので、初期化とか代入とかちょっとした事をやりたくですぐ詰まる。
次回は本の内容に戻りたい