如何在Rust中结合scan与filter去除迭代器的外部可变状态?
问题
我尝试移除迭代器中的外部可变状态。原实现通过filter配合外部可变变量筛选递增最大值:
fn main() { let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9]; let mut max_so_far = 0; for val in non_increasing .iter() .filter(|&&height| if height > max_so_far {max_so_far = height; true} else {false}) { println!("{val}") } } // output // 1 // 2 // 6 // 9
我发现可用scan实现,但难以结合filter,于是采用了以i32::MIN为标记的临时方法(仅当该值不会出现时有效):
fn main() { let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9]; for val in non_increasing .iter() .scan(0, |max_so_far, &x|{ if x > *max_so_far { *max_so_far = x; Some(x) } else { Some(i32::MIN) } }) .filter(|&x| x != i32::MIN) { println!("{val}") } } // output // 1 // 2 // 6 // 9
不过我希望找到非hack的实现方式,类似如下自定义scan_filter的逻辑:
fn main() { let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9]; for val in non_increasing .iter() .scan_filter(0, |max_so_far, &x|{ if x > *max_so_far { *max_so_far = x; (true, Some(x)) } else { (false, Some(x)) } }) { println!("{val}") } } // output // 1 // 2 // 6 // 9
请问该如何实现?
解决方案
你可以通过自定义迭代器适配器实现scan_filter,结合scan的状态管理和filter的筛选能力,完全避免标记值的hack写法。
1. 定义ScanFilter迭代器结构体
首先创建一个结构体封装迭代器、内部状态和处理闭包,然后为它实现Iterator特质:
use std::iter::Iterator; struct ScanFilter<I, St, F> { iter: I, state: St, f: F, } impl<I, St, F, T> Iterator for ScanFilter<I, St, F> where I: Iterator<Item = T>, F: FnMut(&mut St, T) -> (bool, Option<T>), { type Item = T; fn next(&mut self) -> Option<Self::Item> { loop { match self.iter.next() { None => return None, Some(item) => { let (keep, output) = (self.f)(&mut self.state, item); if keep { return output; } } } } } }
2. 添加迭代器扩展方法
为所有迭代器类型实现扩展特质,让scan_filter可以像标准库方法一样直接调用:
trait ScanFilterExt: Iterator { fn scan_filter<St, F>(self, initial_state: St, f: F) -> ScanFilter<Self, St, F> where F: FnMut(&mut St, Self::Item) -> (bool, Option<Self::Item>), Self: Sized, { ScanFilter { iter: self, state: initial_state, f, } } } // 为所有迭代器自动实现扩展特质 impl<I: Iterator> ScanFilterExt for I {}
3. 调用自定义的scan_filter
现在就可以按照你预期的逻辑使用,建议把初始值改为i32::MIN以兼容包含负数的数组:
fn main() { let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9]; for val in non_increasing .iter() .scan_filter(i32::MIN, |max_so_far, &x| { if x > *max_so_far { *max_so_far = x; (true, Some(x)) } else { (false, None) } }) { println!("{val}") } }
输出结果和预期一致:
1 2 6 9
实现说明
- 闭包通过返回
(bool, Option<T>)直接控制元素是否保留以及输出值,无需依赖特殊标记值 - 内部状态(比如
max_so_far)可以在闭包中自由修改,完美结合状态管理和筛选逻辑 - 扩展特质的实现让所有迭代器都能直接调用
scan_filter,用法和标准库迭代器方法一致
内容的提问来源于stack exchange,提问作者Pioneer_11
相关产品推荐
相关产品推荐

