如何将Vec部分折叠到位?

时间:2015-02-04 10:21:56

标签: coding-style rust

我想通过一个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。

这似乎非常复杂和不优雅。

5 个答案:

答案 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));
}