LoginSignup
1
1

More than 5 years have passed since last update.

なぜかRustで言語処理100本ノック ~第2章 後編~

Posted at

前編はこちら

第2章: UNIXコマンドの基礎

16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.

split -l $N hightemp.txt相当なのですが、このコマンドだとファイルが自動生成されるのに対して、今回はVec<String>で返すようにしました(実質等価なので許してください)。スライスを用いればjoinが使えるんですね。

pub fn split_file(path: &Path, n: usize) -> Result<Vec<String>> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let lines = br.lines().collect::<Result<Vec<_>>>();
    lines.and_then(|lines| Ok(lines.chunks(n).map(|chunk| chunk.join("\n")).collect()))
}

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

cat col1.txt | sort | uniqこれと同じ結果になれば良いわけですね。unwrapしないように書いたら結構な長さになってしまいました。

pub fn get_column_differences(path: &Path, n: usize) -> Result<HashSet<String>> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let mut result = HashSet::new();
    br.lines().map(|line|
        line.and_then(|line|
            line.split_whitespace().map(|word| word.to_string()).nth(n)
            .ok_or(Error::new(ErrorKind::NotFound, format!("the column {} is not found.", n)))))
    .for_each(|line| match line {
        Ok(line) => { result.insert(line.to_string()); },
        Err(e) => eprintln!("{}", e)
    });
    Ok(result)
}

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

ファイル内容を見ると実は反転させるだけで結果が一致してしまうのですが、それはそれとして。こまんどのばあいとことなることがあるのは、安定ソートかどうかで結果が少し変わってしまうからですね。一応、相当するコマンドはsort hightemp.txt --key=3です。

pub fn sort_by_column(path: &Path, n: usize) -> Result<Vec<String>> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let lines = br.lines().collect::<Result<Vec<_>>>();
    lines.and_then(|mut lines| {
        lines.sort_by(|a, b| a.split_whitespace().nth(n).cmp(&b.split_whitespace().nth(n)));
        Ok(lines)
    })
}

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

cut --field=1 hightemp.txt | sort | uniq -c | sort -r相当なのですが、今回HashMapを使った影響で同数だと順序不定になるようです。

pub fn sort_by_frequency(path: &Path) -> Result<Vec<String>> {
    let file = File::open(path)?;
    let br = BufReader::new(file);
    let lines = br.lines().collect::<Result<Vec<_>>>();
    let mut counter: HashMap<String, usize> = HashMap::new();
    lines.and_then(|lines|
        Ok(lines.into_iter().for_each(|line| {
            let key = line.split_whitespace().next().unwrap_or("");
            if counter.contains_key(key) {
                *counter.get_mut(key).unwrap() += 1
            } else {
                counter.insert(key.to_string(), 1);
            }
    }))).and_then(|_| {
        let mut tmp = counter.into_iter().collect::<Vec<_>>();
        tmp.sort_by(|a, b| b.1.cmp(&a.1));
        Ok(tmp.into_iter().map(|(k, _)| k).collect())
    })
}

HashMapの更新ってもう少しスッキリ書けないものかと思ってしまいました。

第2章の感想

UNIXコマンドの中でもファイル関係を扱う影響で、またできる限りunwrapしないようにした結果、Result及びOptionの扱い方が自然とわかってきました。この章をやっただけでも十分な収穫な気がします。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1