Rustのitertoolsなんとなく使っていたのですが、
時間とってどんなメソッドやマクロがあるのかなと眺めてみたので恣意的にピックアップしてまとめました。
順列、組み合わせ系
permutations
順列を返却してくれます。
let perms = (5..8).permutations(2);
itertools::assert_equal(perms, vec![
vec![5, 6],
vec![5, 7],
vec![6, 5],
vec![6, 7],
vec![7, 5],
vec![7, 6],
]);
combinations
名前の通り、組み合わせを返却してくれます。
let it = (1..5).combinations(3);
itertools::assert_equal(it, vec![
vec![1, 2, 3],
vec![1, 2, 4],
vec![1, 3, 4],
vec![2, 3, 4],
]);
tuple_combinations
組み合わせをtupleで返却してくれます。
let mut v = Vec::new();
for (a, b) in (1..5).tuple_combinations() {
v.push((a, b));
}
assert_eq!(v, vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]);
combinations_with_replacement
この記事をまとめるきっかけになったメソッドです。(重複組合せっていうらしい)
自前実装してたのですが、他の方の回答でこちら使っていて感動しました。
let it = (1..4).combinations_with_replacement(2);
itertools::assert_equal(it, vec![
vec![1, 1],
vec![1, 2],
vec![1, 3],
vec![2, 2],
vec![2, 3],
vec![3, 3],
]);
ループ系
iproduct!
2重ループや3重ループをすっきりかける。
// Iterate over the coordinates of a 4 x 4 x 4 grid
// from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3)
for (i, j, k) in iproduct!(0..4, 0..4, 0..4) {
// ..
}
izip!
まとめてイテレーションしたいときに使えそう。
// iterate over three sequences side-by-side
let mut results = [0, 0, 0, 0];
let inputs = [3, 7, 9, 6];
for (r, index, input) in izip!(&mut results, 0..10, &inputs) {
*r = index * 10 + input;
}
assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
tuple_windows
基本的にはwindows()
で事足りそうな気もするけど。。
重複なしの場合はtuples<T>(self)
というメソッドも用意されている。
let mut v = Vec::new();
for (a, b) in (1..5).tuple_windows() {
v.push((a, b));
}
assert_eq!(v, vec![(1, 2), (2, 3), (3, 4)]);
cartesian_product
直積。あんまり使う機会はなさそうですが・・・
let it = (0..2).cartesian_product("αβ".chars());
itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]);
インデックス検索系
positions
条件に一致するインデックスをVec<>
で返却してくれます。
enumerate
を使用しなくてもインデックスがわかるのは便利そうな気もします。
let data = vec![1, 2, 3, 3, 4, 6, 7, 9];
itertools::assert_equal(data.iter().positions(|v| v % 2 == 0), vec![1, 4, 5]);
itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]);
find_position
最小の要素を見つけ出すposition_min
、
最大の要素を見つけ出すposition_max
もあったので使えるかも。
どっちも欲しい場合はposition_minmax
もありました。
カスタムで順序制御したい場合のためにposition_min_by
もあったので覚えておくといつか使えるかも。
let text = "Hα";
assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α')));
その他
merge
順序を制御したい場合はfn merge_by<J, F>(self, other: J, is_first: F)
もともとマージソートを自前で実装していたので、merge_by
は便利かも。
let a = (0..11).step(3);
let b = (0..11).step(5);
let it = a.merge(b);
itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]);
unique
重複を飛ばしてくれる(ソートされていなくてもOK)
こちらもfn unique_by<V, F>(self, f: F)
が用意されているので、カスタムして重複を削除できる。
let data = vec![10, 20, 30, 20, 40, 10, 50];
itertools::assert_equal(data.into_iter().unique(), vec![10, 20, 30, 40, 50]);
all_equal
すべてが同じかどうかを判定してくれます。そんなに需要はない気もしますが。。
let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5];
assert!(!data.iter().all_equal());
assert!(data[0..3].iter().all_equal());
assert!(data[3..5].iter().all_equal());
assert!(data[5..8].iter().all_equal());
tree_fold1
ぱっと使用法は思いつきませんでしたが面白かったので転記します笑
// The same tree as above
let num_strings = (1..8).map(|x| x.to_string());
assert_eq!(num_strings.tree_fold1(|x, y| format!("f({}, {})", x, y)),
Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
// Like fold1, an empty iterator produces None
assert_eq!((0..0).tree_fold1(|x, y| x * y), None);
// tree_fold1 matches fold1 for associative operations...
assert_eq!((0..10).tree_fold1(|x, y| x + y),
(0..10).fold1(|x, y| x + y));
// ...but not for non-associative ones
assert_ne!((0..10).tree_fold1(|x, y| x - y),
(0..10).fold1(|x, y| x - y));