P.CTF.01 避免滥用迭代器
【描述】
迭代器虽然是 Rust 中比较推崇的方式,但也没必要过度使用它。总之,如果使用迭代器让代码太复杂,就考虑换个非迭代器的方式实现吧。
【反例】
创建一个 Matrix变换的函数,但是这种迭代器的方式,代码可读性相比于命令式更困难。
#![allow(unused)] fn main() { // From : https://adventures.michaelfbryan.com/posts/rust-best-practices/bad-habits/#overusing-iterators pub fn functional_blur(input: &Matrix) -> Matrix { assert!(input.width >= 3); assert!(input.height >= 3); let mut rows = input.rows(); let first_row = rows.next().unwrap(); let last_row = rows.next_back().unwrap(); let top_row = input.rows(); let middle_row = input.rows().skip(1); let bottom_row = input.rows().skip(2); let blurred_elements = top_row .zip(middle_row) .zip(bottom_row) .flat_map(|((top, middle), bottom)| blur_rows(top, middle, bottom)); let elements: Vec<f32> = first_row .iter() .copied() .chain(blurred_elements) .chain(last_row.iter().copied()) .collect(); Matrix::new_row_major(elements, input.width, input.height) } fn blur_rows<'a>( top_row: &'a [f32], middle_row: &'a [f32], bottom_row: &'a [f32], ) -> impl Iterator<Item = f32> + 'a { // 不符合: 使用迭代器处理矩阵变换,代码不直观 let &first = middle_row.first().unwrap(); let &last = middle_row.last().unwrap(); let top_window = top_row.windows(3); let middle_window = middle_row.windows(3); let bottom_window = bottom_row.windows(3); let averages = top_window .zip(middle_window) .zip(bottom_window) .map(|((top, middle), bottom)| top.iter().chain(middle).chain(bottom).sum::<f32>() / 9.0); std::iter::once(first) .chain(averages) .chain(std::iter::once(last)) } }
【正例】
创建一个 Matrix变换的函数,使用命令式风格,代码功能比较明确,更加直观。
#![allow(unused)] fn main() { // From: https://adventures.michaelfbryan.com/posts/rust-best-practices/bad-habits/#overusing-iterators pub fn imperative_blur(input: &Matrix) -> Matrix { assert!(input.width >= 3); assert!(input.height >= 3); let mut output = input.clone(); for y in 1..(input.height - 1) { for x in 1..(input.width - 1) { let mut pixel_value = 0.0; // 符合: 直接使用数组计算坐标更加直观方便 pixel_value += input[[x - 1, y - 1]]; pixel_value += input[[x, y - 1]]; pixel_value += input[[x + 1, y - 1]]; pixel_value += input[[x - 1, y]]; pixel_value += input[[x, y]]; pixel_value += input[[x + 1, y]]; pixel_value += input[[x - 1, y + 1]]; pixel_value += input[[x, y + 1]]; pixel_value += input[[x + 1, y + 1]]; output[[x, y]] = pixel_value / 9.0; } } output } }