Rust迭代器并期待(peek / multipeek)

时间:2016-06-07 16:16:59

标签: iteration rust

我试图在Rust中使用带有迭代器的模式并在某处掉落,显然很简单。

我想遍历一个容器并找到一个带有谓词[A](简单)的元素,但是然后使用另一个谓词向前看并得到该值[B]并使用[B]来变异[A] in某种方式。在这种情况下,[A]是可变的,[B]可以是不可变的;这对我没有任何影响,只对借阅检查员(正确)。

通过一个简单的场景帮助理解这一点,所以我添加了一个小片段,让民众看到问题/尝试的目标。我玩过itertools并进入for / while循环,虽然我想尽可能保持惯用。

愚蠢的示例方案

查找偶数,找到可被3整除的下一个数字并添加到初始数字。

#[allow(unused)]
fn is_div_3(num: &u8) -> bool {
    num % 3 == 0
}

fn main() {
    let mut data: Vec<u8> = (0..100).collect();

    let count = data.iter_mut()
        .map(|x| {
            if *x % 2 == 0 {
                // loop through numbers forward to next is_div_3,
                // then x = x + that number
            }
            true
        })
        .count();

    println!("data {:?}, count was {} ", data, count);
}

playground

2 个答案:

答案 0 :(得分:5)

可悲的是,我有点迟了,但现在就去了。

它并不完全漂亮,但它没有其他建议那么糟糕:

let mut data: Vec<u8> = (1..100).collect();

{
    let mut mut_items = data.iter_mut();
    while let Some(x) = mut_items.next() {
        if *x % 2 == 0 {
            let slice = mut_items.into_slice();
            *x += *slice.iter().find(|&x| x % 3 == 0).unwrap();
            mut_items = slice.iter_mut();
        }
    }
}

println!("{:?}", data);

给出

[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]

与Matthieu M.的解决方案一样。

关键是使用mut_items.into_slice()来“重新借用”迭代器,有效地生成迭代器的本地(因此是安全的)克隆。

答案 1 :(得分:3)

警告:下面显示的迭代器是unsafe,因为它允许一个多个别名获得一个可变元素;跳到修正版本的第二部分。 (如果返回类型包含不可变引用,那就没关系。)

如果你愿意编写自己的窗口迭代器,那么它就变得非常容易了。

首先,它的所有血腥细节都是迭代器:

use std::marker::PhantomData;

struct WindowIterMut<'a, T>
    where T: 'a
{
    begin: *mut T,
    len: usize,
    index: usize,
    _marker: PhantomData<&'a mut [T]>,
}

impl<'a, T> WindowIterMut<'a, T>
    where T: 'a
{
    pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
        WindowIterMut {
            begin: slice.as_mut_ptr(),
            len: slice.len(),
            index: 0,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for WindowIterMut<'a, T>
    where T: 'a
{
    type Item = (&'a mut [T], &'a mut [T]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.index > self.len { return None; }

        let slice: &'a mut [T] = unsafe {
            std::slice::from_raw_parts_mut(self.begin, self.len)
        };
        let result = slice.split_at_mut(self.index);

        self.index += 1;

        Some(result)
    }
}

[1, 2, 3]上调用它将返回(&[], &[1, 2, 3])然后(&[1], &[2, 3]),......直到(&[1, 2, 3], &[])。简而言之,它迭代切片的所有潜在分区(不需要改组)。

可以安全使用:

fn main() {
    let mut data: Vec<u8> = (1..100).collect();

    for (head, tail) in WindowIterMut::new(&mut data) {
        if let Some(element) = head.last_mut() {
            if *element % 2 == 0 {
                if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
                    *element += *n3;
                }
            }
        }
    }

    println!("{:?}", data);
}

不幸的是它也可以用作:

fn main() {
    let mut data: Vec<u8> = (1..100).collect();

    let mut it = WindowIterMut::new(&mut data);
    let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
    let second_0 = &mut it.next().unwrap().0[0];

    println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}

当运行print:0x7f73a8435000 0x7f73a8435000时,show-casing表示两个可变引用都是同一个元素。

由于我们无法摆脱锯齿,我们需要摆脱可变性;或者至少遵守内部可变性Cell,因为u8Copy)。

幸运的是,Cell没有运行时成本,但它在人体工程学方面确实有些成本(所有.get().set())。

我借此机会使迭代器稍微更通用,并重命名它,因为Window已经是另一个概念的已使用名称。

struct FingerIter<'a, T>
    where T: 'a
{
    begin: *const T,
    len: usize,
    index: usize,
    _marker: PhantomData<&'a [T]>,
}

impl<'a, T> FingerIter<'a, T>
    where T: 'a
{
    pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
        FingerIter {
            begin: slice.as_ptr(),
            len: slice.len(),
            index: 0,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for FingerIter<'a, T>
    where T: 'a
{
    type Item = (&'a [T], &'a T, &'a [T]);

    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.len { return None; }

        let slice: &'a [T] = unsafe {
            std::slice::from_raw_parts(self.begin, self.len)
        };

        self.index += 1;
        let result = slice.split_at(self.index);

        Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
    }
}

我们将它用作建筑砖:

fn main() {
    let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();

    for (_, element, tail) in FingerIter::new(&data) {
        if element.get() % 2 == 0 {
            if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
                element.set(element.get() + n3.get());
            }
        }
    }

    let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();

    println!("{:?}", data);
}

On the playpen打印:[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...],这似乎是正确的。