Skip to content

Instantly share code, notes, and snippets.

@hebiyan
Last active Apr 11, 2021
Embed
What would you like to do?
Rust での読み込み処理を短く書きたい

動機

オンラインジャッジなどの問題では、「n 個の数字を読み込む」コードをしょっちゅう書く。Rust でこれを扱うとき、大体こんなかんじではないか。
(読み込む個数 n は問題で指定されているとする)

/* 標準入力を開いて */
let mut scan = std::io::stdin();

let mut line = String::new();

/* 読み込んで  */
let _ = scan.read_line(&mut line);
/* 空白で分割してからベクタにまとめる */
let vec: Vec<&str> = line.split_whitespace().collect();

/* それぞれを変換 */
let n: i32 = vec[0].parse().unwrap_or(0);
let m: f32 = vec[1].parse().unwrap_or(0.0);
let k: u32 = vec[2].parse().unwrap_or(0);

split_whitespace() が半角スペースでも改行でも分割してくれるので、上のコードで

1
2.0
3

でも、

1 2.0 3

でも読み込むことができる。

ところで唐突だけど Common Lisp で書くならこんなコードになる。

(loop repeat 3 collect (read))

短い。read に読み込みと分割と変換を全部任せられるからだ。 文字列を読み込むのならばこのコードではだめで、もうすこし (プログラマが) 分担しなければならないが、読むのが数字だけとわかっていればこれで十分だ。

Common Lisp でこのようなコードを書くことに慣れていると、Rust のコードは長すぎてつらい。 開いて、read_line して、 split_whitespace して、parse して、という一連の流れに慣れればいい、というのはその通りなのだけど、もうちょっと短くしたい。 具体的にはこういうふうに書きたい。

let (n, m, k) = read_from_stdin(i32, f32, u32);

書いてみた

fn read_from_stdin<T, S, P>() -> (T, S, P)
    where T: std::str::FromStr + Default,
S: std::str::FromStr + Default,
P: std::str::FromStr + Default {
    let mut scan = std::io::stdin();
    let mut line = String::new();
    let _ = scan.read_line(&mut line);

    let vec: Vec<&str> = line.split_whitespace().collect();

	(vec[0].parse::<T>().unwrap_or(Default::default()),
     vec[1].parse::<S>().unwrap_or(Default::default()),
     vec[2].parse::<P>().unwrap_or(Default::default()))
}

これで、

let (n, m, k) = read_from_stdin::<i32, f32, u32>();

と読み込める。

しかし

これは 3 個読み込むのにしか使えない。 2 個のときは 2 個用の関数を別に書く必要がある。だるい。

マクロを使えば解決できるのかもしれないが、今のところ、 vec[0], vec[1], vec[2], ... を扱う方法がわからない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment