LoginSignup
11
3

More than 5 years have passed since last update.

LinqのあれをRustのイテレータでやってみた

Last updated at Posted at 2018-04-24

参考

C#のLinqでGroupByを使っているのならPythonの方が2倍速くなる - Qiita
C# の Linq が python の2倍遅い、は嘘 - Qiita

コード

#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

extern crate csv;
extern crate itertools;
extern crate serde;

use std::fs::File;
//use std::io::prelude::*;
use itertools::Itertools;
use serde::Serializer;

fn main() {
    #[derive(Debug, Deserialize)]
    struct Record(String, String, f64, f64);

    let mut rdr = csv::Reader::from_reader(File::open("test.csv").expect("File open err."));
    let multiply_to_int = |x: f64, y: f64| if x > 0.0 {
        (x * y + 0.0000001) as i32
    } else {
        (x * y - 0.0000001) as i32
    };
    let mut ser = serde_json::Serializer::new(File::create("result.json").expect("File open err."));
    ser.collect_seq(rdr.deserialize()
            .map(|result| result.expect("Bad CSV"))
            .map(|record: Record| (record.0.to_owned(), multiply_to_int(record.2, record.3)))
            .into_group_map()
            .into_iter()
            .map(|(k, v)| json!({"a": k, "z": v.iter().sum::<i32>()})))
        .expect("Serialize err.");
}

実行時間

Python (元記事のを使用)

$ python app.py
所要時間:0.4632906913757324[sec]
$ python app.py
所要時間:0.44978904724121094[sec]
$ python app.py
所要時間:0.4565582275390625[sec]

Rust

$ time ./target/release/foo

real 0m0.339s
user 0m0.331s
sys 0m0.007s
$ time ./target/release/foo

real 0m0.340s
user 0m0.330s
sys 0m0.010s
$ time ./target/release/foo

real 0m0.338s
user 0m0.328s
sys 0m0.010s

感想

  • 速くなるのは分かっててやったので、速度については、こんなもんか程度の感想。
  • イテレータ使い倒したらLinqとそんなに遜色ないコーディング量で書けて、別にそれで遅くなるわけでもないのはRust尊いなーって印象。
  • コンパイル時のライフタイム地獄にはあんまり陥らなかった。型が主にi32f64だったのとStringto_owned()で逃げたためと思われる。
  • それでもやっぱり、型を合わせるのがしんどかった。
  • serde_json、地味に便利。
  • Record型のString&strにしてコピーを避ける方法、探せばある気がするんだけど、分からなかった。
  • itertoolsにgroup_byというそれっぽい関数があるけど、ドキュメント読んだ感じ、SQLのGROUP BYやlinqのGroupByとは挙動違いそう。これ、罠だろ。
ドキュメント中のサンプルコード
// group data into runs of larger than zero or not.
let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
// groups:     |---->|------>|--------->|

// Note: The `&` is significant here, `GroupBy` is iterable
// only by reference. You can also call `.into_iter()` explicitly.
for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
    // Check that the sum of each group is +/- 4.
    assert_eq!(4, group.sum::<i32>().abs());
}
11
3
2

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
11
3