代码之家  ›  专栏  ›  技术社区  ›  Felix

使用函数编程方法迭代网格的(x,y)位置

  •  2
  • Felix  · 技术社区  · 7 年前

    我想迭代位置 (x, y) 在二维网格中,为每个位置调用一个函数(提供 x y 作为参数)。我知道怎么用 for 循环,但我想用函数编程方法编写这个函数,这样我以后可以利用像rayon这样的库。我已经设法构建了一个功能版本,但它似乎很复杂,我想问一下是否有一种更清洁的方法来实现这一点。

    下面是一个计算网格中所有2x2补丁的最大总和的小例子:

    use std::cmp::max;
    
    const WIDTH: usize = 4;
    const HEIGHT: usize = 3;
    
    type Grid = [[u32; WIDTH]; HEIGHT];
    
    fn main() {
        let grid: Grid = [
            [1, 3, 5, 8], 
            [3, 9, 4, 2], 
            [3, 4, 5, 0],
        ];
    
        let coords = (0..WIDTH - 1).flat_map(|x| (0..HEIGHT - 1).map(move |y| (x, y)));
        let max_sum = coords.map(|(x, y)| sum_2x2(x, y, &grid)).max().unwrap();
        println!("Max 2x2 patch: {}", max_sum);
    }
    
    fn sum_2x2(x: usize, y: usize, grid: &Grid) -> u32 {
        [
            grid[y][x],
            grid[y][x + 1],
            grid[y + 1][x],
            grid[y + 1][x + 1],
        ]
        .iter()
        .sum()
    }
    

    线 let coords = let coords = (0..WIDTH - 1).flat_map(|x| (0..HEIGHT - 1).map(move |y| (x, y))); 对于这么简单的任务来说似乎相当复杂。在python中,我会做以下工作来获得位置(我认为这更干净):

    >>> from itertools import product
    >>> product(range(HEIGHT-1), range(WIDTH-1))
    

    有没有更好的方法来写这个,或者我只是需要习惯它?

    3 回复  |  直到 7 年前
        1
  •  3
  •   Jmb    7 年前

    您可以使用类似这样的循环理解板条箱来简化代码 map_for mdo . 例如, 地图 :

    let max_sum = map_for!(
        move;
        x <- 0..WIDTH-1;
        y <- 0..HEIGHT-1;
        => sum_2x2(x, y, &grid)
    ).max().unwrap();
    

    我没试过用人造丝,但从那以后 地图符号 只是一系列调用的语法糖 map flat_map 并且与实现这些功能的任何类型一起工作,它也应该与人造丝一起工作。

    全面披露:我是 地图符号 机箱。

        2
  •  3
  •   JayDepp    7 年前

    另一个选择是使用 cartesian_product 从itertools板条箱,它有许多方便的方法涉及迭代器。

    use itertools::Itertools;
    
    (0..WIDTH-1).cartesian_product(0..HEIGHT-1).map(|(x, y)| sum_2x2(x, y, &grid)).max()
    
        3
  •  0
  •   Felix    7 年前

    正如@hellow在评论中指出的,有一个 iproduct 宏在 itertools 做我想要的。使用它,我的代码可以这样重写:

    use itertools::iproduct;
    ...
    let coords = iproduct!(0..WIDTH - 1, 0..HEIGHT - 1);
    ...
    

    这正是我要找的。感谢所有在这里评论/发布答案的人。

    PS:如果要将ITertools迭代器与Rayon一起使用,可以使用 par_bridge 方法,例如 iproduct!(0..WIDTH - 1, 0..HEIGHT - 1).par_iter(); . 从那以后我花了一段时间才弄明白 par_iter into_par_iter 不要工作。

    推荐文章