Rust中的折叠问题

3
假设有一个名为 fn scan(int, int) -> int 的函数。
在使用该函数时,
fn count(x: int, y: int) -> int
{
    scan(x - 1, y - 1) + scan(x - 1, y) + scan(x - 1, y + 1) + scan(x, y - 1)
        + scan(x, y + 1) + scan(x + 1, y - 1) + scan(x + 1, y) + scan(x + 1, y + 1)
}

我得到了正确的结果。我正在尝试通过将scan函数折叠在给定的值范围上来获得相同的结果,但似乎无法做到正确。我的当前尝试是:

fn count_fold(x: int, y: int) -> int
{
    std::iter::range_inclusive(-1, 1).zip(std::iter::range_inclusive(-1, 1)).fold(0, |a, (i, j)| { a + scan(x + i, y + j) })
}

似乎我只得到了正确结果的一部分。我做错了什么吗?谢谢您的帮助。
2个回答

5
当您对两个迭代器进行zip操作时,您并没有创建所需的迭代产物。相反,您正在同时迭代两个迭代器,并使用已迭代的值创建一对值。因此,在count_fold版本中,闭包将仅使用以下一对值调用:
(-1, -1)
(0, 0)
(1, 1)

所以你的 count_fold 函数实际上类似于

scan(x - 1, y - 1) + scan(x, y) + scan(x - 1, y + 1)

可能我说错了,但我认为在std中没有一个函数可以创建两个迭代器的乘积。

此外,您的count方法并未在求和时使用scan(x, y),因此它甚至不是真正的迭代器乘积;如果要创建自己的乘积迭代器并将其用于此目的,您必须小心谨慎。


为了澄清一下,我不需要在那里使用 scan(x, y)。这个想法是扫描给定坐标的Moore邻域。 - Arets Paeglis

4

Cyrille说没有迭代器乘积函数是正确的。然而,可以通过两次折叠手动地取两个迭代器的乘积:

use std::iter::range_inclusive;

fn count_fold(x: int, y: int) -> int {
    range_inclusive(-1, 1).fold(0, 
         |a, i| range_inclusive(-1, 1).fold(a, |b, j| b + scan(x + i, y + j)))
}

虽然看起来你需要在 i == 0 && j == 0 这种情况下进行过滤,也就是说,

fn count_fold(x: int, y: int) -> int {
    range_inclusive(-1, 1).fold(0, 
         |a, i| range_inclusive(-1, 1).fold(a, 
             |b, j| if i == 0 && j == 0 {b} else {b + scan(x + i, y + j)}))
}

或者

fn count_fold(x: int, y: int) -> int {
    range_inclusive(-1, 1).fold(0, 
         |a, i| range_inclusive(-1, 1)
             .filter(|&j| !(i == 0 && j == 0))
             .fold(a, |b, j| b + scan(x + i, y + j)))
}

不过,我几乎可以说这是更加明确的命令式语句:
fn count_fold(x: int, y: int) -> int {
    let mut a = 0;
    for i in range_inclusive(-1, 1) {
        for j in range_inclusive(-1, 1) {
            if !(i == 0 && j == 0) { a += scan(x + i, y + j) }
        }
    }
    a
}

很奇怪。你的命令式版本,就像我的一样,产生了正确的结果,但你的函数式版本不起作用(此外,“scan”过滤了当“i == 0 && j == 0”时的情况,所以“count”不需要这样做)。 - Arets Paeglis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接