【问题标题】:How to zip two iterators of unequal length with a default?如何使用默认压缩两个长度不等的迭代器?
【发布时间】:2019-12-12 04:28:33
【问题描述】:

我正在尝试压缩两个长度不等的迭代器,它仅在两者都有值时返回,并忽略最长迭代器中的其余部分。

fn main() {
    let num1 = vec![1, 2];
    let num2 = vec![3];

    for i in num1.iter().rev().zip(num2.iter().rev()) {
        println!("{:?}", i);
    }
}

这将返回 (2, 3)。我如何让它返回:

(2, 3)
(1, 0) // default is the 0 here.

还有其他方法吗?

【问题讨论】:

  • 感谢编辑,将代码附在 main fn 中。
  • 你知道哪个最长吗?
  • 好吧,我至少可以找出来,并在最长的时间内调用 iter。另一件事是,即使我在 num2 而不是 num1 上进行 iter 调用,它也会在任何一个迭代器耗尽时停止。

标签: rust iterator


【解决方案1】:

只要其中一个迭代器停止生成值,Zip 就会停止。如果你知道哪个是最长的,你可以用你的默认值填充较短的那个:

use std::iter;

fn main() {
    let longer = vec![1, 2];
    let shorter = vec![3];

    for i in longer
        .iter()
        .rev()
        .zip(shorter.iter().rev().chain(iter::repeat(&0)))
    {
        println!("{:?}", i);
    }
}

如果你不知道哪个最长,你应该使用itertools,如Peter Varo suggests

【讨论】:

  • 两个答案都很好。你的回答对我有用,喜欢它,因为它不用任何外部板条箱就可以工作。但现在,我接受@Peter Varo 的回答。
【解决方案2】:

您可以使用itertools crate 提供的zip_longest

use itertools::{
    Itertools,
    EitherOrBoth::*,
};

fn main() {
    let num1 = vec![1, 2];
    let num2 = vec![3];

    for pair in num1.iter().rev().zip_longest(num2.iter().rev()) {
        match pair {
            Both(l, r) => println!("({:?}, {:?})", l, r),
            Left(l) => println!("({:?}, 0)", l),
            Right(r) => println!("(0, {:?})", r),
        }
    }
}

这将产生以下输出:

(2, 3)
(1, 0)

【讨论】:

    【解决方案3】:

    关键是要检测一个迭代器比另一个更短,你可以在你的案例向量实现ExactSizeIterator之前这样做,但一般的解决方案是有一个自定义的.zip()

    itertools已经提供了一个通用的解决方案,.zip_longest()

    use itertools::EitherOrBoth::{Both, Left, Right};
    use itertools::Itertools;
    
    fn main() {
        let num1 = vec![1, 2];
        let num2 = vec![3];
    
        for i in num1
            .iter()
            .rev()
            .zip_longest(num2.iter().rev())
            .map(|x| match x {
                Both(a, b) => (a, b),
                Left(a) => (a, &0),
                Right(b) => (&0, b),
            })
        {
            println!("{:?}", i);
        }
    }
    

    这需要您每次都编写闭包,如果您经常需要此功能,可以使用.zip_default() 在迭代器上实现自定义特征,其中AB 实现Default

    use std::default::Default;
    use std::iter::Fuse;
    
    pub trait MyIterTools: Iterator {
        fn zip_default<J>(self, other: J) -> ZipDefault<Self, J::IntoIter>
        where
            J: IntoIterator,
            Self: Sized,
        {
            ZipDefault::new(self, other.into_iter())
        }
    }
    
    #[derive(Clone, Debug)]
    pub struct ZipDefault<I, J> {
        i: Fuse<I>,
        j: Fuse<J>,
    }
    
    impl<I, J> ZipDefault<I, J>
    where
        I: Iterator,
        J: Iterator,
    {
        fn new(i: I, j: J) -> Self {
            Self {
                i: i.fuse(),
                j: j.fuse(),
            }
        }
    }
    
    impl<T, U, A, B> Iterator for ZipDefault<T, U>
    where
        T: Iterator<Item = A>,
        U: Iterator<Item = B>,
        A: Default,
        B: Default,
    {
        type Item = (A, B);
    
        fn next(&mut self) -> Option<Self::Item> {
            match (self.i.next(), self.j.next()) {
                (Some(a), Some(b)) => Some((a, b)),
                (Some(a), None) => Some((a, B::default())),
                (None, Some(b)) => Some((A::default(), b)),
                (None, None) => None,
            }
        }
    }
    
    impl<T: ?Sized> MyIterTools for T where T: Iterator {}
    
    fn main() {
        let num1 = vec![1, 2];
        let num2 = vec![3];
    
        for i in num1
            .iter()
            .copied()
            .rev()
            .zip_default(num2.iter().copied().rev())
        {
            println!("{:?}", i);
        }
    }
    

    使用 itertools 我们可以委托一些逻辑:

    use std::default::Default;
    use itertools::Itertools;
    use itertools::ZipLongest;
    use itertools::EitherOrBoth::{Both, Left, Right};
    
    pub trait MyIterTools: Iterator {
        fn zip_default<J>(self, j: J) -> ZipDefault<Self, J::IntoIter>
        where
            Self: Sized,
            J: IntoIterator,
        {
            ZipDefault::new(self, j.into_iter())
        }
    }
    
    #[derive(Clone, Debug)]
    pub struct ZipDefault<I, J> {
        inner: ZipLongest<I, J>,
    }
    
    impl<I, J> ZipDefault<I, J>
    where
        I: Iterator,
        J: Iterator,
    {
        fn new(i: I, j: J) -> Self {
            Self {
                inner: i.zip_longest(j),
            }
        }
    }
    
    impl<T, U, A, B> Iterator for ZipDefault<T, U>
    where
        T: Iterator<Item = A>,
        U: Iterator<Item = B>,
        A: Default,
        B: Default,
    {
        type Item = (A, B);
    
        fn next(&mut self) -> Option<Self::Item> {
            match self.inner.next()? {
                Both(a, b) => Some((a, b)),
                Left(a) => Some((a, B::default())),
                Right(b) => Some((A::default(), b)),
            }
        }
    
        fn size_hint(&self) -> (usize, Option<usize>) {
            self.inner.size_hint()
        }
    }
    
    impl<T: ?Sized> MyIterTools for T where T: Iterator {}
    
    fn main() {
        let num1 = vec![1, 2];
        let num2 = vec![3];
    
        for i in num1
            .iter()
            .copied()
            .rev()
            .zip_default(num2.iter().copied().rev())
        {
            println!("{:?}", i);
        }
    }
    

    【讨论】:

    • 哇,非常感谢您提供如此详细的代码。来自动态语言的 Rust 新手,非常喜欢它。
    【解决方案4】:

    如果你能得到迭代器的长度,就像在这种情况下,一个快速而肮脏的方法可能是:

    use std::iter::repeat;
    
    fn main() {
        let a = vec![1, 2, 3];
        let b = vec![4, 5, 6, 7];
    
        for i in a
            .iter()
            .rev()
            .chain(repeat(&0).take(b.len().saturating_sub(a.len())))
            .zip(
                b.iter()
                    .rev()
                    .chain(repeat(&0).take(a.len().saturating_sub(b.len()))),
            )
        {
            println!("{:?}", i);
        }
    }
    

    您还可以使用这种方法实现包含zip_default() 的特征:

    
    pub trait MyIterTools<X: Default + Clone>: ExactSizeIterator<Item = X> {
        fn zip_default<J, Y>(self, j: J) -> ZipDefault<Self, J::IntoIter, X, Y>
        where
            Self: Sized,
            J: IntoIterator<Item = Y>,
            J::IntoIter: ExactSizeIterator,
            Y: Default + Clone,
        {
            ZipDefault::new(self, j.into_iter())
        }
    }
    
    #[derive(Clone, Debug)]
    pub struct ZipDefault<
        I: ExactSizeIterator<Item = X>,
        J: ExactSizeIterator<Item = Y>,
        X: Default + Clone,
        Y: Default + Clone,
    > {
        inner: Zip<Chain<I, Take<Repeat<X>>>, Chain<J, Take<Repeat<Y>>>>,
    }
    
    impl<
            I: ExactSizeIterator<Item = X>,
            J: ExactSizeIterator<Item = Y>,
            X: Default + Clone,
            Y: Default + Clone,
        > ZipDefault<I, J, X, Y>
    {
        fn new(a: I, b: J) -> Self {
            let a_len = a.len();
            let b_len = b.len();
            Self {
                inner: a
                    .chain(repeat(X::default()).take(b_len.saturating_sub(a_len)))
                    .zip(b.chain(repeat(Y::default()).take(a_len.saturating_sub(b_len)))),
            }
        }
    }
    
    impl<
            I: ExactSizeIterator<Item = X>,
            J: ExactSizeIterator<Item = Y>,
            X: Default + Clone,
            Y: Default + Clone,
        > Iterator for ZipDefault<I, J, X, Y>
    {
        type Item = (X, Y);
    
        fn next(&mut self) -> Option<Self::Item> {
            self.inner.next()
        }
    
        fn size_hint(&self) -> (usize, Option<usize>) {
            self.inner.size_hint()
        }
    }
    
    impl<T: ExactSizeIterator<Item = X>, X: Default + Clone> MyIterTools<X> for T {}
    
    fn main() {
        let a = vec![1, 2, 3];
        let b = vec![4, 5, 6, 7];
    
        a.into_iter()
            .zip_default(b.into_iter())
            .for_each(|i| println!("{:?}", i));
    }
    

    【讨论】:

      【解决方案5】:

      我在 leetcode 解决方案的其他人代码中看到了这个巧妙的技巧。如果您可以访问长度,则可以交换迭代器,使 iter1 最长。

      fn main() {
          let num1 = vec![1, 2];
          let num2 = vec![3];
          let mut iter1 = num1.iter();
          let mut iter2 = num2.iter();
          if iter1.len() < iter2.len(){
              std::mem::swap(&mut iter1, &mut iter2);
          } // now iter1 is the largest
          for i in iter1.rev().zip(iter2.rev().chain(std::iter::repeat(&0))) {
              println!("{:?}", i);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-22
        • 1970-01-01
        • 2015-08-28
        • 2013-08-01
        • 2019-02-20
        • 2017-10-15
        • 1970-01-01
        相关资源
        最近更新 更多