Problem 1
引数
当然1000は固定でなく、引数で指定したいですよね。そのようにコードを変更してみました。
use std::env; fn main() { let args: Vec<String> = env::args().collect(); let n: i64 = args[1].parse().unwrap(); let mut s = 0; for i in 1..n { if i % 3 == 0 || i % 5 == 0 { s += i; } } println!("{}", s); }
use std::env;
モジュールをインポートしているようです。std::envは引数を取るのに必要です。
let args: Vec<String> = env::args().collect();
引数を配列に格納しています。
env:argsは、引数のiteratorを返すんですね。それをcollectで配列にすると。
ここで、Vecは可変長配列で、その中身の方がStringというわけです。
エラー処理
let n: i64 = args[1].parse().unwrap();
parseで文字列を整数にするわけですが、もし整数にならなかった場合、Rustでは例外を投げるのではなく、Errを返します。
より正確には、unwrapはここではResult<i64,E>を返します。Result<T>は、Err<E>か、Ok<T>のどちらか取ります。Haskellのようにマッチングが使えて、
let n: i64; match args[1].parse() { Ok(val) => n = val, Err(err) => panic!("error : {}", err), }
ここで、整数にならない文字列を引数にすると、
$ ./e001a 1000. thread 'main' panicked at 'error : invalid digit found in string', e001a.rs:8:15 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
と、Err(err)にマッチしてそれ以降を処理するわけです。
ただ、たかがProject Eulerの問題を解くためにこんなエラー処理をしているわけにいかないので、Ok(val)であると仮定して、unwrapでひっぺ返して、valにするわけです。最初のコードだと、
$ ./e001a 1000. thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:1165:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
よく分からないところでエラーになってしまいます。
速度
さて、ここでC++と速度を比較してみましょう。C++で同様に書いて、
#include <iostream> using namespace std; int main(int argc, char **argv) { const int n = atoi(argv[1]); long long s = 0; for(int i = 1; i < n; ++i) { if(i%3 == 0 || i%5 == 0) { s += i; } } cout << s << endl; }
$ g++ -O2 e001.cpp $ time ./a.out 1000000000 233333333166666668 real 0m1.133s user 0m1.125s sys 0m0.000s
Rustも最適化オプションをつけて、
$ rustc -O e001a.rs $ time ./e001a 1000000000 233333333166666668 real 0m1.221s user 0m1.203s sys 0m0.000s
差は確かにあるようですね。