如何将 Vec 部分折叠到位?
How do I partially fold a Vec in place?
我想遍历 Vec 并组合其中的一些元素。我如何在 idiomatic Rust 中做到这一点?
示例:
#[derive(PartialEq, Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
// idiomatic code here
}
fn main() {
let v = vec![Thing::A, Thing::B];
assert_eq!(vec![Thing::AandB], combine(v));
}
我会怎么做:
用Iterator::scan遍历Vec,如果Thing::A是之前的元素,用Thing::AandB替换所有出现的Thing::B。然后我会再次遍历它并删除 Thing::AandB.
之前的所有 Thing::As
这看起来超级复杂和不优雅。
不确定这是否算作惯用,但是 itertools 库具有适用于所有迭代器的 batching()
函数。结合标准库中的 peek()
,您只需一次迭代即可获得结果,而不是两次。
extern crate itertools;
use itertools::Itertools;
use Thing::*;
#[derive(PartialEq, Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
v.into_iter().peekable().batching(|mut it| {
match it.next() {
Some(A) => {
if Some(&B) == it.peek() {
it.next();
Some(AandB)
} else {
Some(A)
}
}
x => x,
}
}).collect()
}
fn main() {
let v = vec![A, B, A, A, A, B, B, A];
assert_eq!(
vec![AandB, A, A, AandB, B, A],
combine(v)
);
}
显然 collect()
will allocate a new buffer.
我怀疑没有简单的方法可以用迭代器做到这一点,但没有人对普通的旧 c 风格设置禁运:
#[derive(PartialEq, Debug, Copy)]
enum Thing { A, B, AandB }
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
let mut prev: Option<Thing> = None;
let mut end = 0;
for i in 0 .. v.len() {
let el = v[i];
match (el, prev) {
(Thing::B, Some(Thing::A)) => {
end = end - 1;
v[end] = Thing::AandB
},
_ =>
v[end] = el
};
prev = Some(el);
end = end + 1;
}
v.truncate(end);
v
}
fn main() {
let v = vec![Thing::A, Thing::A, Thing::B, Thing::AandB, Thing::B, Thing::A];
assert_eq!(vec![Thing::A, Thing::AandB, Thing::AandB, Thing::B, Thing::A], combine(v));
}
这是一个直接转换的过程。
这是一个使用递归和模式匹配的解决方案。我很确定递归是尾递归,因此可以变成迭代。
use Thing::*;
#[derive(Copy,Clone,PartialEq,Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
fn inner(mut res: Vec<Thing>, s: &[Thing]) -> Vec<Thing> {
match s {
[A, B, tail..] => {
res.push(AandB);
inner(res, tail)
},
[a, tail..] => {
res.push(a);
inner(res, tail)
},
[] => res,
}
};
inner(Vec::new(), &v)
}
fn main() {
let v = vec![A, A, B, AandB, B, A];
assert_eq!(vec![A, AandB, AandB, B, A], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, B];
assert_eq!(vec![A, AandB, AandB, B, AandB, AandB], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, A];
assert_eq!(vec![A, AandB, AandB, B, AandB, A, A], combine(v));
}
我合并了 swizard 的答案和 Shepmaster 的答案,最终得到了一个就地解决方案,该解决方案递归地运行向量,只有向量是可变的,并且从不移动任何东西两次。不保证运行时或惯用性 ;)
use Thing::*;
use std::cmp::min;
#[derive(Copy,Clone,PartialEq,Debug)]
enum Thing { A, B, AandB}
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
fn inner(res: &mut Vec<Thing>, i: usize, backshift: usize) {
match &res[i..min(i+2, res.len())] {
[A, B] => {
res[i - backshift] = AandB;
inner(res, i + 2, backshift + 1);
},
[a, ..] => {
res[i - backshift] = a;
inner(res, i + 1, backshift);
},
[] => res.truncate(i - backshift),
}
};
inner(&mut v, 0, 0);
v
}
fn main() {
let v = vec![A, A, B, AandB, B, A, B, A, B];
assert_eq!(vec![A, AandB, AandB, B, AandB, AandB], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, A];
assert_eq!(vec![A, AandB, AandB, B, AandB, A, A], combine(v));
}
好的,这是一个没有显式 for 循环和递归的惯用版本:)
#[derive(PartialEq, Debug, Copy)]
enum Thing { A, B, AandB }
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
let (_, total) = (0 .. v.len()).fold((None, 0), |&mut: (prev, end), i| {
let el = v[i];
let (next, item) = match (el, prev) {
(Thing::B, Some(Thing::A)) => (end, Thing::AandB),
_ => (end + 1, el),
};
v[next - 1] = item;
(Some(el), next)
});
v.truncate(total);
v
}
fn main() {
let v = vec![Thing::A, Thing::A, Thing::B, Thing::AandB, Thing::B, Thing::A];
assert_eq!(vec![Thing::A, Thing::AandB, Thing::AandB, Thing::B, Thing::A], combine(v));
}
我想遍历 Vec 并组合其中的一些元素。我如何在 idiomatic Rust 中做到这一点?
示例:
#[derive(PartialEq, Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
// idiomatic code here
}
fn main() {
let v = vec![Thing::A, Thing::B];
assert_eq!(vec![Thing::AandB], combine(v));
}
我会怎么做:
用Iterator::scan遍历Vec,如果Thing::A是之前的元素,用Thing::AandB替换所有出现的Thing::B。然后我会再次遍历它并删除 Thing::AandB.
之前的所有 Thing::As这看起来超级复杂和不优雅。
不确定这是否算作惯用,但是 itertools 库具有适用于所有迭代器的 batching()
函数。结合标准库中的 peek()
,您只需一次迭代即可获得结果,而不是两次。
extern crate itertools;
use itertools::Itertools;
use Thing::*;
#[derive(PartialEq, Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
v.into_iter().peekable().batching(|mut it| {
match it.next() {
Some(A) => {
if Some(&B) == it.peek() {
it.next();
Some(AandB)
} else {
Some(A)
}
}
x => x,
}
}).collect()
}
fn main() {
let v = vec![A, B, A, A, A, B, B, A];
assert_eq!(
vec![AandB, A, A, AandB, B, A],
combine(v)
);
}
显然 collect()
will allocate a new buffer.
我怀疑没有简单的方法可以用迭代器做到这一点,但没有人对普通的旧 c 风格设置禁运:
#[derive(PartialEq, Debug, Copy)]
enum Thing { A, B, AandB }
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
let mut prev: Option<Thing> = None;
let mut end = 0;
for i in 0 .. v.len() {
let el = v[i];
match (el, prev) {
(Thing::B, Some(Thing::A)) => {
end = end - 1;
v[end] = Thing::AandB
},
_ =>
v[end] = el
};
prev = Some(el);
end = end + 1;
}
v.truncate(end);
v
}
fn main() {
let v = vec![Thing::A, Thing::A, Thing::B, Thing::AandB, Thing::B, Thing::A];
assert_eq!(vec![Thing::A, Thing::AandB, Thing::AandB, Thing::B, Thing::A], combine(v));
}
这是一个直接转换的过程。
这是一个使用递归和模式匹配的解决方案。我很确定递归是尾递归,因此可以变成迭代。
use Thing::*;
#[derive(Copy,Clone,PartialEq,Debug)]
enum Thing { A, B, AandB }
fn combine(v: Vec<Thing>) -> Vec<Thing> {
fn inner(mut res: Vec<Thing>, s: &[Thing]) -> Vec<Thing> {
match s {
[A, B, tail..] => {
res.push(AandB);
inner(res, tail)
},
[a, tail..] => {
res.push(a);
inner(res, tail)
},
[] => res,
}
};
inner(Vec::new(), &v)
}
fn main() {
let v = vec![A, A, B, AandB, B, A];
assert_eq!(vec![A, AandB, AandB, B, A], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, B];
assert_eq!(vec![A, AandB, AandB, B, AandB, AandB], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, A];
assert_eq!(vec![A, AandB, AandB, B, AandB, A, A], combine(v));
}
我合并了 swizard 的答案和 Shepmaster 的答案,最终得到了一个就地解决方案,该解决方案递归地运行向量,只有向量是可变的,并且从不移动任何东西两次。不保证运行时或惯用性 ;)
use Thing::*;
use std::cmp::min;
#[derive(Copy,Clone,PartialEq,Debug)]
enum Thing { A, B, AandB}
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
fn inner(res: &mut Vec<Thing>, i: usize, backshift: usize) {
match &res[i..min(i+2, res.len())] {
[A, B] => {
res[i - backshift] = AandB;
inner(res, i + 2, backshift + 1);
},
[a, ..] => {
res[i - backshift] = a;
inner(res, i + 1, backshift);
},
[] => res.truncate(i - backshift),
}
};
inner(&mut v, 0, 0);
v
}
fn main() {
let v = vec![A, A, B, AandB, B, A, B, A, B];
assert_eq!(vec![A, AandB, AandB, B, AandB, AandB], combine(v));
let v = vec![A, A, B, AandB, B, A, B, A, A];
assert_eq!(vec![A, AandB, AandB, B, AandB, A, A], combine(v));
}
好的,这是一个没有显式 for 循环和递归的惯用版本:)
#[derive(PartialEq, Debug, Copy)]
enum Thing { A, B, AandB }
fn combine(mut v: Vec<Thing>) -> Vec<Thing> {
let (_, total) = (0 .. v.len()).fold((None, 0), |&mut: (prev, end), i| {
let el = v[i];
let (next, item) = match (el, prev) {
(Thing::B, Some(Thing::A)) => (end, Thing::AandB),
_ => (end + 1, el),
};
v[next - 1] = item;
(Some(el), next)
});
v.truncate(total);
v
}
fn main() {
let v = vec![Thing::A, Thing::A, Thing::B, Thing::AandB, Thing::B, Thing::A];
assert_eq!(vec![Thing::A, Thing::AandB, Thing::AandB, Thing::B, Thing::A], combine(v));
}