NLP100ノック第2章

第2章を一通り終えたので書きます。

10. 行数のカウント

タイトルのままです。 Rust では std::str::Linescount() がありますので利用しておわりです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let mut args = env::args();

    args.next();
    for path in args {
        let f = File::open(path).unwrap();
        let br = BufReader::new(f);

        println!("{}", br.lines().count());
    }
}

11. タブをスペースに置換

コチラも、std::string::String.replace() 利用するだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let mut args = env::args();
    args.next();
    for path in args {
        let file = File::open(path).unwrap();
        let reader = BufReader::new(file);

        for line in reader.lines() {
            println!("{}", line.unwrap().replace("\t", " "));
        }
    }
}

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

こちらは単純にファイルに書き込むのと、分割ができれば問題ないです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;

fn main() {
    let mut args = env::args();

    args.next();
    for path in args {
        let f = path.clone().replace(".txt", "");
        let s = path.clone().replace(".txt", "");
        let file = File::open(path).unwrap();
        let br = BufReader::new(file);
        let mut first_column = BufWriter::new(File::create(format!("{}_col1.txt", f)).unwrap());
        let mut second_column = BufWriter::new(File::create(format!("{}_col2.txt", s)).unwrap());

        for line in br.lines() {
            let words = line.unwrap().split("\t").map(|m| m.to_string()).collect::<Vec<String>>();
            first_column.write(format!("{}\n", words[0]).as_bytes()).unwrap();
            second_column.write(format!("{}\n", words[1]).as_bytes()).unwrap();
        }
    }
}

13. col1.txtとcol2.txtをマージ

こちらは以前利用した、zip があれば問題ないです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;

fn main() {
    let mut args = env::args();
    if args.len() < 2 { panic!("col1.txt col2.txt"); }

    args.next();

    let first = args.next().unwrap();
    let second = args.next().unwrap();

    let fr = BufReader::new(File::open(first).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();

    let sr = BufReader::new(File::open(second).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();
    let mut merge_file =
        BufWriter::new(File::create("merge.txt".to_string()).unwrap());

    for (x, y) in fr.iter().zip(&sr) {
        merge_file.write(format!("{}\t{}\n", x, y).as_bytes()).unwrap();
    }

}

14. 先頭からN行を出力

head コマンドですので std::iter::Iterator.take() を利用するだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();

    let br = BufReader::new(file).lines().take((&args[1]).to_string().parse::<usize>().unwrap());

    for line in br.map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
        println!("{}", line);
    }
}

15. 末尾のN行を出力

tail コマンドです。こちらは std::iter::Iterator.skip() を利用してやるだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();
    let takes = (&args[1]).to_string().parse::<usize>().unwrap();
    let br = BufReader::new(&file).lines();
    let skips = br.count() - takes;
    let file = File::open(&args[0]).unwrap();
    let lines = BufReader::new(&file).lines().skip(skips);

    for line in lines {
        println!("{}", line.unwrap());
    }
}

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

こちらの実装は素朴な実装とし、行数で分割しております。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let count: usize = BufReader::new(file).lines().count();
    let div: usize = (&args[1]).to_string().parse().unwrap();
    let file = File::open(path).unwrap();
    let mut br = BufReader::new(file).lines();

    let t = (count as f64/ div as f64).ceil() as usize;

    for x in 1 .. div + 1 {
        let l = br.by_ref();
        let file = File::create(format!("{}.txt",x)).unwrap();
        let mut bw = BufWriter::new(file);
        for y in l.take(t).map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
            let a = format!("{}\n", y);
            bw.write(a.as_bytes()).unwrap();
            println!("{}: {}", x, y);
        }
    }
}

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

ファイル読込 + HashSet で実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

extern crate regex;
use regex::Regex;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let re = Regex::new(r"\W+").unwrap();
    let hs = BufReader::new(file).lines().map(|m|{
        let l = m.unwrap().clone();
        re.split(&l).next().unwrap().to_string()
    }).collect::<HashSet<_>>();

    for s in hs {
        println!("{}", s);
    }

}

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

こちら、実数の比較を行う必要があり、すこし めんどう でした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let mut val = BufReader::new(file).lines().map(|m| m.unwrap().split("\t").skip(2).next().unwrap().parse::<f64>().unwrap()).collect::<Vec<f64>>();

    val.sort_by(|a, b| a.partial_cmp(b).unwrap());
    for v in val { println!("{}", v); }
}

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

18の問題を更にカウントできるように変更した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

enum Value {
    USIZE(usize),
    NONE(()),
}

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();
    let mut words: HashMap<String, usize> = HashMap::new();
    for m in BufReader::new(&file).lines() {
        let w = m.unwrap().to_string().split("\t").next().unwrap().to_string();
        let v = match words.get(&w) {
            None => 1,
            Some(n) => n + 1,
        };
        words.insert(w, v);
    }

    let mut vars: Vec<(&String, &usize)> = words.iter().collect();
    vars.sort_by(|a, b| b.1.cmp(a.1));
    for (w, v) in vars {println!("{}: {}", w, v);}

}

おわり

この章は慣れてきたのか比較的楽に解けています。