从 Rust 向量中间进行单个循环迭代

3
我有一个元素向量,想要从某个索引开始迭代,并循环地再次访问每个元素,但只访问一次。
例如,从索引2开始:
[0, 1, 2, 3, 4, 5, 6]
       ^

我希望能够获得一个迭代器,它可以遍历元素 [2, 3, 4, 5, 6, 0, 1] (并且避免在需要这样运行向量的每个地方都编写循环)。标准的 cycle() + skip() 遍历似乎是个不错的起点,但它显然永远不会结束。

是否有任何符合 Rust 标准迭代器的惯用方法?


1
为什么不直接切片并使用Iterator::chain将两个部分连接起来呢?例如: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=16cee881bd70ef5675e5caa6f89872d9 - Masklinn
1
也许是 .cycle().skip().take(v.len()) - user4815162342
2个回答

3
您可以迭代这两个子切片,并使用 chain 将它们连接成一个迭代器:
let v = vec![0, 1, 2, 3, 4, 5, 6];
let start_index = 2;
for e in v[start_index..].iter().chain(v[..start_index].iter()) {
    println!("{}", e);
}

2
显而易见,修复您的循环/跳过组合的方法是添加 take() 来限制它:
fn cycle<T>(slice: &[T], start_pos: usize) -> impl Iterator<Item = &T> {
    slice.iter().cycle().skip(start_pos).take(slice.len())
}

另外一种选择是仅仅连接这两个范围,这样甚至可以使代码变得更短:
fn cycle<T>(slice: &[T], start_pos: usize) -> impl Iterator<Item = &T> {
    slice[start_pos..].iter().chain(&slice[..start_pos])
}

两个版本都通过了以下测试:
let v = vec![0, 1, 2, 3, 4, 5, 6];
assert_eq!(cycle(&v, 2).copied().collect::<Vec<_>>(), vec![2, 3, 4, 5, 6, 0, 1]);

cycle().skip().take() 解决方案的开销非常小。对于切片,Iter 类型特殊处理 nth()(由 skip() 默认实现使用),因此不需要循环,而 Cycle 具有智能的 advance_by() 实现(由 nth() 的默认实现使用)。开销根本不取决于实际索引 - 它是恒定的,编译器甚至可能能够消除它。 - Sven Marnach
@SvenMarnach,我不知道有关于nth/skip优化的事情,谢谢你指出来。我已经编辑了回答中关于不必要调用next()的说法。 - user4815162342
@user4815162342,我真的很喜欢第一个解决方案。是的 - 现在你指出要使用 take(),它似乎如此明显 :-) 最后,我还需要添加一个 enumerate(),其中包含“原始”索引,这样可以很好地配合使用。 - Troels

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