首页 文章

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

提问于
浏览
10

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

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

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

Silly Example scenario

查找偶数,找到可被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 回答

  • 3

    Warning: The iterator presented right below is unsafe because it allows one to obtain multiple aliases to a single mutable element; skip to the second part for the corrected version. (如果返回类型包含不可变引用,那就没关系) .

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

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

    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, ...] ,这似乎是正确的 .

  • 4

    可悲的是我有点晚了,但是到了 .

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

    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() 到"reborrow"迭代器,有效地生成迭代器的本地(因此是安全的)克隆 .

相关问题