Help us understand the problem. What is going on with this article?

RustでJavaみたいなScanner

More than 1 year has passed since last update.

目的

競技プログラミングでRustを使うときに入力がとても面倒に感じ、JavaのScanner・C++のcinのようにトークンを一つ読み込んでパースして返すようなクラスがあったら便利だと思い作ってみました。

実装

※ 改良版を下に追記しました (2017/01/06)

yukicoder No.349 干支の置き物 にてAcceptされたコードです。
Scanner部分のアルゴリズムはchokudaiさんのコード(C#)を参考にしました。

まず sc: Scanner を宣言します。一行目に整数 $n$ が、続く $n$ 行にアルファベットからなる文字列が与えられるので、sc.next()sc.wrapped() によって順番に読み込んでいます。

僕はよく手元でテストケースをひとつのファイルにまとめ、./solver < inputのようにしてテストするので、while let Ok(n) = sc.wrapped()としています。

use std::collections::*;

fn main(){
    let mut sc = Scanner::new();
    while let Ok(n) = sc.wrapped() {
        let mut cnt = HashMap::new();
        for _ in 0..n {
            let s:String = sc.next();
            match cnt.get(&s).cloned() {
                Some(k) => cnt.insert(s, k+1),
                _ => cnt.insert(s, 1),
            };
        };

        let mut max = 0;
        for (_, val) in cnt.iter() {
            if &max < val {
                max = val.clone();
            }
        }
        println!("{}", if max <= (n+1)/2 { "YES" } else { "NO" });
    }
}

// Scanner

#[allow(dead_code)]
struct Scanner {
    token_buffer : Vec<String>,
    index : usize,
}

#[allow(dead_code)]
impl Scanner {
    fn new() -> Scanner{
        Scanner { token_buffer: vec![], index: 0 }
    }

    fn wrapped<T>(& mut self) -> Result<T,&str> where T: std::str::FromStr {
        let s = try!(self.fetch_token());
        let t = try!(s.parse::<T>().map_err(|_| "Parse error"));
        Ok(t)
    }
    fn next<T>(& mut self) -> T where T: std::str::FromStr {
        self.wrapped::<T>().unwrap()
    }

    fn fetch_token(&mut self) -> Result<&String,&str> {
        while self.index >= self.token_buffer.len() {
            let mut st = String::new();
            while st.trim() == "" {
                match std::io::stdin().read_line(&mut st) {
                    Ok(l) if l > 0 => continue,
                    Ok(_)  => return Err("End of file"),
                    Err(_) => return Err("Failed to read line"),
                }
            }
            self.token_buffer = st.split_whitespace()
                .map(|x| x.to_string())
                .collect();
            self.index = 0;
        }

        self.index += 1;
        Ok(&self.token_buffer[self.index - 1])
    }
}

std::io::stdin().read_line()EOFに到達してもErrを返さずにOk(0) (OKの中は読み込んだ文字の数)を返す仕様に嵌りました。どういう意図があるんでしょう?

Rust初心者なのでこうした方がいいよという所があったら教えてください…

追記 (2017/02/13)

どんどん書き直しているのでここにある最新版を使った方が良いかもしれません -> http://tubo28.me/algorithm/scanner-rs/

上のコードとの差分

  • EOFまで読み込む処理が書きづらく,さらに毎回splitしているので若干遅いという問題があったのでget_charベースに
  • std:: io::Fileなどの任意のReadトレイトの実装を持つ型を受け入れるようにした
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away