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

问题描述 投票:0回答:6

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

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.

还有其他方法吗?

rust iterator
6个回答
23
投票

您可以使用

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),
        }
    }
}

这将产生以下输出:

(2, 3)
(1, 0)

19
投票

一旦其中一个迭代器停止生成值,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 建议的那样


9
投票

关键是检测一个迭代器比另一个迭代器短,您可以在案例向量实现之前执行此操作

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()
在迭代器上实现自定义特征,其中
A
B
实现
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);
    }
}

4
投票

我在 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);
    }
}

1
投票

如果您可以获得迭代器的长度,就像在本例中一样,一个快速而肮脏的方法可能是:

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));
}

0
投票

不管怎样,我发布了我针对 LeetCode 问题整理的简单

ZipLongest
解决方案。 LC 不提供 itertools 箱。这种类型可以轻松放入 LC 编辑器中并使用。

struct ZipLongest<I, E> {
    iter1: I,
    iter2: I,
    fill : E,
}
impl<I, E> ZipLongest<I, E> {
    fn new(iter1: I, iter2: I, fill: E) -> Self {
        Self { iter1, iter2, fill }
    }
}

impl<I: Iterator<Item=E>, E: Clone> Iterator for ZipLongest<I, E> {
    type Item = (E, E);

    fn next(&mut self) -> Option<Self::Item> {
        match (self.iter1.next(), self.iter2.next()) {
            (Some(a), Some(b)) => Some((a, b)),
            (Some(a), None   ) => Some((a, self.fill.clone())),
            (None,    Some(a)) => Some((self.fill.clone(), a)),
            (None,    None   ) => None,
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.