【问题标题】:How can I zip more than two iterators?如何压缩两个以上的迭代器?
【发布时间】:2015-06-22 12:56:12
【问题描述】:

是否有更直接和可读的方式来完成以下操作:

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 个迭代器中构建一个迭代器?

【问题讨论】:

    标签: iterator rust


    【解决方案1】:

    您可以使用 crate 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 = "0.8"
    

    【讨论】:

    • 我喜欢。暂时接受,除非有人有来自std的东西@
    • 你能解压其中一个并获得一组集合吗?
    • 是的,请参阅Iterator 上的 .unzip()(不过,仅涵盖配对情况)。
    • 问题是你需要事先知道参数的数量。在 python 中你可以zip(*list_of_tuples) 得到一个任意长列表的结果
    • @GChamon 这对于 Rust 元组是不可能的,因为在编译时必须知道 Rust 元组中元素的数量和类型。
    【解决方案2】:

    您还可以使用提供的.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
    

    【讨论】:

      【解决方案3】:

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

      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
      }
      

      【讨论】:

      • 这不是zip 迭代器,所以它似乎不是这个问题的答案。
      • Rust 不支持可变参数,这里的 Vec 只是作为一个包。但它是将 N 个大小为 M 的向量映射到 M 个大小为 N 的向量,其中每个向量的第一个元素来自第一个向量,第二个来自第二个向量,依此类推。我如何将其推广到 rust 中的所有迭代器类,而不是使用向量?另外,感谢您花时间参考该参考资料,我学到了一些新东西
      • @Shepmaster 这是如何将此 Python 代码转换为 rust 的答案:list(zip(*[[1,2,3],[4,5,6],[7,8,9]]))。因此,虽然它实际上并没有使用 zip,但它完成了像我这样的人期望 zip 做的事情。因此,它是对该问题的某些解释的有用答案。
      • @BlackShift 我不同意。对于一组有限的可能输入值,它仅相当于您的 Python 示例(例如,它不适用于混合类型,例如 list(zip(*[[1,2,3],["a","b","c"],[2.5, 3.7, 7.6]])))。因此,虽然对于“如何转置 m 向量的 n 向量?”这个问题,这将是一个完美的答案。这不是当前问题的答案。此外,问题是关于没有将结果存储在容器中的迭代器。再次用 Python 术语来说,相当于 zip(*[[1,2,3],["a","b","c"],[2.5, 3.7, 7.6]]) 而不从中生成列表。
      猜你喜欢
      • 2015-08-28
      • 2013-08-01
      • 2019-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-05
      相关资源
      最近更新 更多