在Rust中,是否有一种自然的方法可以迭代几个范围或迭代器的“乘积”?
当您遍历多维数组或者状态空间时会遇到这种情况。例如,我想考虑一个由5个元素组成的布尔元组的所有可能值。嵌套5个for
循环有点笨拙。
itertools crate有一个非常人性化的宏(iproduct!
),用于迭代迭代器的乘积。以下是一个示例:
pub fn main() {
let a = ['A', 'B', 'C'];
let b = [1, 4];
let c = [true, false];
let d = ['x', 'y'];
for (a, b, c, d) in itertools::iproduct!(&a, &b, &c, &d) {
println!("{} {} {} {}", a, b, c, d);
}
}
这里是一个可以完成任务的宏:
macro_rules! product {
($first:ident, $($next:ident),*) => (
$first.iter() $(
.flat_map(|e| std::iter::repeat(e)
.zip($next.iter()))
)*
);
}
fn main() {
let a = ['A', 'B', 'C'];
let b = [1, 4];
let c = [true, false];
let d = ['x', 'y'];
for (((a, b), c), d) in product![a, b, c, d] {
println!("{} {} {} {}", a, b, c, d);
}
}
输出:
A 1 true x
A 1 true y
A 1 false x
A 1 false y
A 4 true x
A 4 true y
etc...
该宏展开为以下代码
a.iter()
.flat_map(|e| std::iter::repeat(e).zip(b.iter()))
.flat_map(|e| std::iter::repeat(e).zip(c.iter()))
.flat_map(|e| std::iter::repeat(e).zip(d.iter()))
flat_map(|e| ... )
将一系列迭代器组合成一个迭代器,其中e
是由迭代器产生的元素。
std::iter::repeat(e)
创建一个重复产生e
的迭代器。
.zip( ... )
同时迭代两个迭代器,并将两者的元素作为一对产生。
宏需要解释更多,最好阅读书中的宏章节。a
中的每个元素重新创建b
迭代器,并为a
b
中每对元素重新创建c
迭代器,等等。因此,如果其中任何一个消耗了值、很昂贵或只应调用一次,那么这种策略就不能像预期那样工作。(然而,一般情况下很难生成这样的迭代器,至少不是在首先收集到第二个数据结构(如Vec
)之前,我想大多数迭代器都很容易创建,因此这些问题似乎并不特别严重。) - huonstd::iter::Repeat::new
更改为 std::iter::repeat
,工作代码:https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=7ff9db3c7f53253a89992c2303f4374b - giuliano-oliveira
std::bitset
上进行“迭代”(计数)。 - Mooing Duckitertools.product
实现。然而,在Rust中实现这样的功能并不像一开始看起来那么容易。通常情况下,实现它需要使用宏。 - Chris Morgan