如何使用默认值压缩两个不等长的迭代器?

blu*_*ggy 3 iterator rust

我正在尝试压缩两个不等长的迭代器,仅当两个都有值时才返回,而忽略最长迭代器中的其余值。

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

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

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

(2, 3)
(1, 0) // default is the 0 here.
Run Code Online (Sandbox Code Playgroud)

还有其他方法吗?

Pet*_*all 11

只要迭代器之一停止生成值,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你不知道哪个最长,你应该使用 itertools,正如Peter Varo 建议的那样

  • 两个答案都很好。你的答案对我有用,喜欢它,因为它不需要使用任何外部板条箱就能工作。但现在,我接受@Peter Varo 的回答。 (2认同)

Pet*_*aro 10

您可以使用板条箱zip_longest提供的itertools

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

这将产生以下输出:

(2, 3)
(1, 0)
Run Code Online (Sandbox Code Playgroud)


Sta*_*eur 5

关键是要检测到一个迭代器比另一个迭代器短,在使用vector实现之前,您可以在做之前,ExactSizeIterator但是一般的解决方案是使用custom .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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这需要您每次都编写闭包,如果您需要此功能,则可能需要在.zip_default()其中带有where ABImplement的迭代器上实现自定义特征Default

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

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

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

  • 哇,非常感谢您提供如此详细的代码。Rust的新手来自动态语言,并且非常喜欢它。 (2认同)