rust 如何有条件地链接迭代器?

5vf7fwbs  于 2022-11-24  发布在  其他
关注(0)|答案(3)|浏览(136)

假设我有:

let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();

然后,我有布尔条件ijk。我想生成一个迭代器,根据ijk的值,有条件地将itjtkt链接在一起。我可以只使用内置的Rust Iterator功能来实现这一点吗?

dgenwo3n

dgenwo3n1#

你可以把Option变成一个迭代器。

let it = i.then_some([1, 2, 3]).into_iter().flatten();
let jt = j.then_some([4, 5, 6]).into_iter().flatten();
let kt = k.then_some([7, 8, 9]).into_iter().flatten();
let iter = it.chain(jt).chain(kt);

如果条件为false,则condition.then_some(...)将返回None,生成空迭代器。否则返回Some(...)into_iter().flatten()Option<impl IntoIterator<Item=T>>转换为impl Iterator<Item=T>

nbewdwxp

nbewdwxp2#

如果你想使用裸迭代器,你会遇到一个小问题:
如果您编写以下内容:

let iter = [1, 2, 3].into_iter();
let iter = if some_condition {
  iter.chain([4, 5, 6])
} else {
  iter
}

您将得到一个错误,可归结为:

= note: expected struct `std::iter::Chain<std::array::IntoIter<_, _>, std::array::IntoIter<{integer}, 3>>`
             found struct `std::array::IntoIter<_, _>`

iter具有IntoIter类型,但iter.chain()具有Chain<IntoIter, ...>类型
要解决此问题,您有以下几种选择:

  • 您可以使用trait对象,它的行为有点像Java等语言中的interface,但会损失一些性能:
let iter = [1, 2, 3].into_iter();
let mut iter: Box<dyn Iterator<Item = i32>> = Box::new(iter);
if some_condition {
  iter = Box::new(iter.chain([4, 5, 6]));
}
  • 或者,如果可以牺牲惰性,可能更好的解决方案是使用Vec
// save heap allocations by pre-allocating the whole vec
let len = if some_condition { 6 } else { 3 };  
let mut items = Vec::with_capacity(len);

items.extend([1, 2, 3]);
if some_condition {
  items.extend([4, 5, 6]);
}
zaqlnxep

zaqlnxep3#

这是either crate的一个很好的用法。当左边和右边都实现Iterator时,Either实现Iterator,所以它可以很容易地用于将迭代器链接在一起。
给定任意三个迭代器itjtkt,它们在相同的Item上迭代,并伴随布尔值ijk,你可以编写一个函数,将它们链接在一起,如下所示:

use either::Either;
use std::iter;

fn chain<'a, I, J, K, Item>(
    it: I,
    jt: J,
    kt: K,
    i: bool,
    j: bool,
    k: bool,
) -> iter::Chain<
    iter::Chain<Either<I, iter::Empty<Item>>, Either<J, iter::Empty<Item>>>,
    Either<K, iter::Empty<Item>>,
>
where
    I: Iterator<Item = Item>,
    J: Iterator<Item = Item>,
    K: Iterator<Item = Item>,
{
    let iter = if i {
        Either::Left(it)
    } else {
        Either::Right(iter::empty())
    };
    let iter = iter.chain(if j {
        Either::Left(jt)
    } else {
        Either::Right(iter::empty())
    });
    let iter = iter.chain(if k {
        Either::Left(kt)
    } else {
        Either::Right(iter::empty())
    });
    iter
}

调用此函数将生成一个基于输入的迭代器条件。

let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();

chain(it, jt, kt, true, false, true).collect::<Vec<_>>();

给予

[1, 2, 3, 7, 8, 9]

如所期望的那样。
您可以使用此playground来尝试。

相关问题