如何保留带有原始索引的向量元素?
How can I retain vector elements with their original index?
如果我有 Vec
,我可以通过 v.iter().enumerate()
使用索引迭代元素,并且我可以通过 v.retain()
删除元素。有没有办法同时做这两件事?
在这种情况下,索引不能再用于访问元素 - 它将是循环开始之前元素的索引。
我可以自己实现,但要像 .retain()
一样高效,我需要使用 unsafe
,我想避免这种情况。
这是我想要的结果:
let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 4, 7, 8];
v.iter()
.retain_with_index(|(index, item)| (index % 2 == 0) || item == 4);
assert(v == vec![1, 3, 4, 5, 4, 7]);
如果你想枚举、过滤(保留),然后收集生成的向量,那么我会说要做到这一点:
v.iter()
.enumerate()
.filter(|&(idx, &val)| val - idx > 0)
.collect()
我基本上发现 the same question on the Rust User's Forum。他们提出了这个解决方案,这还不错:
let mut index = 0;
v.retain(|item| {
index += 1;
((index - 1) % 2 == 0) || item == 4
});
当时它不是一个有效的解决方案,因为无法保证 retain()
的迭代顺序,但令我高兴的是,该线程中的某个人记录了该顺序,所以现在它是。 :-)
@Timmmm's and @Hauleth 的回答很务实我想提供几个备选方案。
这是一个包含一些基准测试和测试的 playground:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cffc3c39c4b33d981a1a034f3a092e7b
这很难看,但如果你真的想要一个 v.retain_with_index()
方法,你可以对 retain
方法进行一些复制粘贴,并添加一个新特征:
trait IndexedRetain<T> {
fn retain_with_index<F>(&mut self, f: F)
where
F: FnMut(usize, &T) -> bool;
}
impl<T> IndexedRetain<T> for Vec<T> {
fn retain_with_index<F>(&mut self, mut f: F)
where
F: FnMut(usize, &T) -> bool, // the signature of the callback changes
{
let len = self.len();
let mut del = 0;
{
let v = &mut **self;
for i in 0..len {
// only implementation change here
if !f(i, &v[i]) {
del += 1;
} else if del > 0 {
v.swap(i - del, i);
}
}
}
if del > 0 {
self.truncate(len - del);
}
}
}
这样的例子看起来像这样:
v.retain_with_index(|index, item| (index % 2 == 0) || item == 4);
或者...更好的是,您可以使用高阶函数:
fn with_index<T, F>(mut f: F) -> impl FnMut(&T) -> bool
where
F: FnMut(usize, &T) -> bool,
{
let mut i = 0;
move |item| (f(i, item), i += 1).0
}
这样的例子现在看起来像这样:
v.retain(with_index(|index, item| (index % 2 == 0) || item == 4));
(我更喜欢后者)
如果我有 Vec
,我可以通过 v.iter().enumerate()
使用索引迭代元素,并且我可以通过 v.retain()
删除元素。有没有办法同时做这两件事?
在这种情况下,索引不能再用于访问元素 - 它将是循环开始之前元素的索引。
我可以自己实现,但要像 .retain()
一样高效,我需要使用 unsafe
,我想避免这种情况。
这是我想要的结果:
let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 4, 7, 8];
v.iter()
.retain_with_index(|(index, item)| (index % 2 == 0) || item == 4);
assert(v == vec![1, 3, 4, 5, 4, 7]);
如果你想枚举、过滤(保留),然后收集生成的向量,那么我会说要做到这一点:
v.iter()
.enumerate()
.filter(|&(idx, &val)| val - idx > 0)
.collect()
我基本上发现 the same question on the Rust User's Forum。他们提出了这个解决方案,这还不错:
let mut index = 0;
v.retain(|item| {
index += 1;
((index - 1) % 2 == 0) || item == 4
});
当时它不是一个有效的解决方案,因为无法保证 retain()
的迭代顺序,但令我高兴的是,该线程中的某个人记录了该顺序,所以现在它是。 :-)
@Timmmm's and @Hauleth 的回答很务实我想提供几个备选方案。
这是一个包含一些基准测试和测试的 playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cffc3c39c4b33d981a1a034f3a092e7b
这很难看,但如果你真的想要一个 v.retain_with_index()
方法,你可以对 retain
方法进行一些复制粘贴,并添加一个新特征:
trait IndexedRetain<T> {
fn retain_with_index<F>(&mut self, f: F)
where
F: FnMut(usize, &T) -> bool;
}
impl<T> IndexedRetain<T> for Vec<T> {
fn retain_with_index<F>(&mut self, mut f: F)
where
F: FnMut(usize, &T) -> bool, // the signature of the callback changes
{
let len = self.len();
let mut del = 0;
{
let v = &mut **self;
for i in 0..len {
// only implementation change here
if !f(i, &v[i]) {
del += 1;
} else if del > 0 {
v.swap(i - del, i);
}
}
}
if del > 0 {
self.truncate(len - del);
}
}
}
这样的例子看起来像这样:
v.retain_with_index(|index, item| (index % 2 == 0) || item == 4);
或者...更好的是,您可以使用高阶函数:
fn with_index<T, F>(mut f: F) -> impl FnMut(&T) -> bool
where
F: FnMut(usize, &T) -> bool,
{
let mut i = 0;
move |item| (f(i, item), i += 1).0
}
这样的例子现在看起来像这样:
v.retain(with_index(|index, item| (index % 2 == 0) || item == 4));
(我更喜欢后者)