Итерация по (x, y) позициям сетки с использованием методов функционального программирования - PullRequest
0 голосов
/ 11 декабря 2018

Я хочу перебрать позиции (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 ]

0 голосов
/ 11 декабря 2018

Другой вариант - использовать cartesian_product из ящика Itertools, который имеет много удобных методов с использованием итераторов.

use itertools::Itertools;

(0..WIDTH-1).cartesian_product(0..HEIGHT-1).map(|(x, y)| sum_2x2(x, y, &grid)).max()
0 голосов
/ 12 декабря 2018

Как отметил @hellow в комментариях, в ящике itertools есть макрос iproduct, который делает то, что я хочу.Используя его, мой код можно переписать так:

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 не работают.

0 голосов
/ 11 декабря 2018

Вы можете упростить код с помощью одного из ящиков с пониманием цикла, таких как map_for или mdo.Например, с map_for:

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

Я не пробовал это с Rayon, но поскольку map_for является просто синтаксическим сахаром для последовательности вызовов map и flat_map и работает сЛюбой тип, который реализует эти функции, должен работать и с Районом.

Полное раскрытие: я являюсь автором map_for ящика.

...