我想通过一个Vec并结合它的一些元素。我如何在惯用 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 :: AandB替换所有出现的Thing :: B,如果Thing :: A是之前的元素。然后我会再次遍历它并删除所有Thing :: As Thing :: AandB。
这似乎非常复杂和不优雅。
答案 0 :(得分:4)
我合并了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));
}
答案 1 :(得分:3)
不确定这是否算作惯用语,但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。
答案 2 :(得分:3)
这是一个使用递归和模式匹配的解决方案。我很确定递归是尾递归,因此可以转换为迭代。
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));
}
答案 3 :(得分:1)
我怀疑使用迭代器没有简单的方法可以做到这一点,但没有人对普通的旧式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));
}
这是一次直接转换。
答案 4 :(得分:1)
好的,这是一个惯用的版本,然后没有明确的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));
}