如何迭代几个范围或迭代器的产品?

sta*_*wed 8 iterator tuples rust

在Rust中有一种自然的方法来迭代几个范围或迭代器的"产品"吗?

当您在多维数组或某些状态空间上进行迭代时,会出现这种情况.例如,我想考虑具有5个元素的布尔元组的所有可能值.嵌套5个for循环有点笨拙.

A.B*_*.B. 9

这是一个完成工作的宏:

#![feature(macro_rules)]

macro_rules! product(
    ($first:ident, $($next:ident),*) => (
        $first.iter() $(
            .flat_map(|e| std::iter::Repeat::new(e)
                .zip($next.iter()))
        )*
    );
)

fn main() {
    let a = ['A', 'B', 'C'];
    let b = [1i, 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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...
Run Code Online (Sandbox Code Playgroud)

围栏的例子

宏扩展到以下

a.iter()
    .flat_map(|e| std::iter::Repeat::new(e).zip(b.iter()))
    .flat_map(|e| std::iter::Repeat::new(e).zip(c.iter()))
    .flat_map(|e| std::iter::Repeat::new(e).zip(d.iter()))
Run Code Online (Sandbox Code Playgroud)

flat_map(|e| ... )将一系列迭代器组合到迭代器中.这e是迭代器产生的元素.

std::iter::Repeat::new(e)创建一个重复的迭代器e.

.zip( ... ) 同时迭代两个迭代器,产生两个元素作为一对.

宏的解释时间有点长,所以最好阅读宏指南

我不知道这是否是最好的方法.

  • 值得注意的是,这会为`a`的每个元素重新创建`b`迭代器,并为`a``b`等中的每对元素重新创建`c`迭代器,所以如果其中任何一个消耗了该值,或者是昂贵的,或者只应该被召唤一次,那么这种策略并不一定按预期工作.(但是,一般来说,实际上不可能生成这样的迭代器,至少,不是没有先收集到辅助数据结构,如`Vec`,我想大多数迭代器都很便宜,所以这些看起来似乎不大特别有问题.) (4认同)

mal*_*rbo 6

itertools箱子有一个非常符合人体工程学的宏(iproduct!用于遍历迭代的产品).这是一个例子:

#[macro_use]
extern crate itertools;

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 iproduct!(&a, &b, &c, &d) {
        println!("{} {} {} {}", a, b, c, d);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • `itertools` 中还有 [`cartesian_product` 函数](https://bluss.github.io/rust-itertools/doc/itertools/trait.Itertools.html#method.cartesian_product)。 (2认同)