如何压缩超过两个迭代器?

59

有没有更直接、更易读的方法来完成以下任务:

fn main() {
    let a = [1, 2, 3];
    let b = [4, 5, 6];
    let c = [7, 8, 9];
    let iter = a.iter()
        .zip(b.iter())
        .zip(c.iter())
        .map(|((x, y), z)| (x, y, z));
}

也就是说,我怎么可以从n个可迭代对象构建一个迭代器,该迭代器生成n元组?

4个回答

83

您可以使用来自itertools创建的 izip!() 宏,它可以为任意多个迭代器实现此功能:

use itertools::izip;

fn main() {

    let a = [1, 2, 3];
    let b = [4, 5, 6];
    let c = [7, 8, 9];

    // izip!() accepts iterators and/or values with IntoIterator.
    for (x, y, z) in izip!(&a, &b, &c) {

    }
}

您需要在Cargo.toml中添加对itertools的依赖项,使用最新版本。例如:

[dependencies]
itertools = "最新版本"

[dependencies]
itertools = "0.8"

我喜欢。暂时接受,除非有人有来自std的东西。 - anderspitman
你能解压其中一个并获取一个集合元组吗? - bright-star
是的,请查看 Iterator 上的 .unzip() 方法(只针对一对情况)。 - bluss
2
问题在于你需要事先知道参数的数量。在Python中,你可以只需使用zip(*list_of_tuples),并得到一个任意长度列表的结果。 - GChamon
1
@GChamon 在 Rust 元组中是不可能实现的,因为元组内部元素的数量和类型必须在编译时确定。 - soulsource

11
你也可以使用提供的 .zip 来创建宏,例如:
$ cat z.rs
macro_rules! zip {
    ($x: expr) => ($x);
    ($x: expr, $($y: expr), +) => (
        $x.iter().zip(
            zip!($($y), +))
    )
}


fn main() {
    let x = vec![1,2,3];
    let y = vec![4,5,6];
    let z = vec![7,8,9];

    let zipped = zip!(x, y, z);
    println!("{:?}", zipped);
    for (a, (b, c)) in zipped {
        println!("{} {} {}", a, b, c);
    }
}

输出:

$ rustc z.rs && ./z
Zip { a: Iter([1, 2, 3]), b: Zip { a: Iter([4, 5, 6, 67]), b: IntoIter([7, 8, 9]), index: 0, len: 0 }, index: 0, len: 0 }
1 4 7
2 5 8
3 6 9

1

我希望能够对任意长度的向量执行此操作,因此我不得不手动实现:

fn transpose_records<T: Clone>(records: &Vec<Vec<T>>) -> Vec<Vec<T>> {
    let mut transposed: Vec<Vec<T>> = vec![Vec::new(); records[0].len()];

    for record in records {
        for (index, element) in record.iter().enumerate() {
            transposed[index].push(element.clone());
        }
    }

    transposed
}

1
这不会对迭代器进行压缩,因此它似乎不是这个问题的答案。 - Shepmaster
为什么不建议将String(&String)、Vec(&Vec)或Box(&Box)的引用作为函数参数接受? - Shepmaster
Rust不支持可变参数,这里的Vec只是作为一个容器。但它将大小为M的N个向量映射到大小为N的M个向量中,其中每个向量的第一个元素来自第一个向量,第二个元素来自第二个向量,以此类推。我该如何将其推广到Rust中所有迭代器类别,而不是使用向量?另外,感谢您抽出时间参考那个参考文献,我学到了新东西。 - GChamon
如果我能轻松地构建生成器,那么构建函数会更好,但是 yield 仍然是实验性的。 - GChamon
1
@Shepmaster 这是一个如何将这段Python代码翻译成Rust的答案:list(zip(*[[1,2,3],[4,5,6],[7,8,9]]))。虽然它实际上并没有使用zip,但它做了像我这样的人期望zip所做的事情。因此,它对于某些问题的解释是有用的。 - BlackShift
1
@BlackShift 我不同意。它只对一组有限的可能输入值等效于你的 Python 示例(例如,它不能处理混合类型,比如 list(zip(*[[1,2,3],["a","b","c"],[2.5, 3.7, 7.6]])))。因此,虽然它是针对问题“如何转置一个 n 向量的 m 向量?”的完美答案,但它并不是当前问题的答案。 此外,这个问题是关于迭代器而不是将结果存储在容器中。再次用 Python 的术语来说,相当于 zip(*[[1,2,3],["a","b","c"],[2.5, 3.7, 7.6]]) 而不生成列表。 - soulsource

-4

现在您可以在没有任何外部依赖的情况下执行以下操作:

for (inp, want) in inputs.iter().zip(wants.iter()) {
   // ...
}

这种方法还支持不同类型。


3
这并没有回答问题。问题涉及到超过两个迭代器,而这只提供了两个迭代器的答案。 - zeh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接