如何将迭代器从末尾开始与自身交织?

4
我有一个形如0..=63的迭代器,即
0 1 2 3 4 ... 59 60 61 62 63
它的.count()为64。
如何获得以下迭代器:
0 63 1 62 2 61 3 60 4 59 ...
(当然与迭代器中存在的项无关),最好不要克隆?
只应更改项目的顺序,.count()应保持不变。
我查阅了标准库和itertools创建物,但未找到相关内容。

你可以使用迭代器适配器来完成一些操作,但我可能会编写自定义迭代器。这并不难。 - Chayim Friedman
@ChayimFriedman 为什么我想要编写自定义迭代器?我有另一个迭代器,我想执行此操作。 - leo848
我指的是自定义迭代器适配器。 - Chayim Friedman
但是我的疯狂要求我这样做:这里 - Chayim Friedman
或者更好的是 这个链接 - Chayim Friedman
3个回答

5

以下是只使用标准库的一种方式。它需要一个 DoubleEndedIterator,并且对于奇数长度的迭代器将跳过最后一个项目:

fn main() {
    let mut range = (0..=63).into_iter();
    let iter = std::iter::from_fn(|| Some([range.next()?, range.next_back()?])).flatten();
    dbg!(iter.collect::<Vec<_>>());
}

输出:

[src/main.rs:4] iter.collect::<Vec<_>>() = [
    0,
    63,
    1,
    62,
    2,
    61,
    3,
...

    30,
    33,
    31,
    32,
]

游乐场


如果您的输入有奇数项,@Finomnis 已经发布了解决方案。


我认为跳过一个元素的事实是危险的,可能会导致非常微妙的错误。特别是如果人们只是从这个答案中复制粘贴。这是您解决方案的一个版本,不会跳过奇数范围的最后一个元素: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ec03e9e3b788daa5f5b1121a546b1c76也许您可以将其某种方式合并到答案中。 - Finomnis
我添加了自己的答案,如果您将其合并到您的答案中,我会删除它。 - Finomnis
1
@Finomnis,对于通用情况来说,你的解决方案比我的好多了。你应该保留它——我会编辑我的答案并包含一个链接。 - Dogbert

3

这个解决方案适用于所有实现了DoubleEndedIterator的迭代器:

请注意,该解决方案保证返回所有项,无论迭代器包含偶数个还是奇数个项。

fn selfinterlace<Iter>(mut iter: Iter) -> impl Iterator<Item = Iter::Item>
where
    Iter: DoubleEndedIterator,
{
    let mut from_front = false;

    std::iter::from_fn(move || {
        from_front = !from_front;
        if from_front {
            iter.next()
        } else {
            iter.next_back()
        }
    })
}

fn main() {
    let range = (0..=8).into_iter();
    let iter = selfinterlace(range);
    println!("{:?}", iter.collect::<Vec<_>>());
}

[0, 8, 1, 7, 2, 6, 3, 5, 4]

该想法是存储下一个项目应该来自前面还是后面的信息,然后在每次迭代中翻转它。

from_fn 可以使用 FnMut,这意味着它可以使用存储内部状态的闭包。此闭包中的内部状态由变量 iterfrom_front 组成,通过 move || 关键字将它们移入闭包中。


2

您可以使用itertools::interleave来交错正向和反向迭代器。

  • RangeInclusive<i32>没有实现ExactSizedIterator,因此没有.len()函数。我们必须自己计算长度。
  • 如果范围的长度为奇数,则多出的项将出现在前半部分,这要归功于(len + 1)
let range = 0..=63;
let len = range.end() - range.start() + 1;
let iter = itertools::interleave(
    range.clone().take((len + 1) / 2),
    range.clone().rev().take(len / 2),
);

Playground


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接