Swift: 用闭包实现Reduce函数

4
以下是我很难理解的代码:
  let rectToDisplay = self.treasures.reduce(MKMapRectNull)  { //1
    (mapRect: MKMapRect, treasure: Treasure) -> MKMapRect in //2

     let treasurePointRect = MKMapRect(origin: treasure.location.mapPoint, size: MKMapSize(width: 0, height: 0)) //3

       return MKMapRectUnion(mapRect, treasurePointRect)
    }

我对reduce函数的理解是:

var people [] // an array of objects
var ageSum = 0
ageSum = people.reduce(0) { $0 + $1.age}

//(0) = initial value
//$0 = running total
//$1 = an object in an array

我对闭包的理解是:

{ (params) -> returnType in
  statements
}

我对上面代码的理解如下: //1 = reduce函数的初始值被设置为(MKMapRectNull) //2 = 传入一个闭包,该闭包带有两个参数而不是一个运行总数和一个数组中的对象,并返回一个MKMapRect
(mapRect: MKMapRect, treasure: Treasure) -> MKMapRect 

//3 = 我卡在这里了。 一个叫做MKMapRect的结构体被调用,它有两个参数origin: treasure.location.mapPointsize: MKMapSize(width: 0, height: 0)

问题1:如果传入的值是0,0MKMapSize将如何计算?它如何获取后续的值并添加它们?

问题2:当此行被返回到//2闭包return MKMapRectUnion(mapRect, treasurePointRect)时,它如何成为运行总数,以及它如何知道要获取self.treasures的下一个元素?

2个回答

7

答案2:

第二(第三、第四)次调用闭包的第一个参数是前一次调用闭包输出的结果。唯一的例外是第一次调用,因为没有前一次调用可以继承,这就是为什么reduce将值0作为第二个参数 - 它是一个特殊的值,用于输入第一次调用。

让我们想象以下场景 - 你有一个数字数组:

let a = [1,2,3]

如果您想使用reduce函数求和:

let sum = reduce(a,0) { (accumulator, nextValue) in accumulator + nextValue }

那么让我们看看每次调用会发生什么:

call#    acc    next    result
1        0      1       0+1 -> 1       # the zero is from reduce's second arg
2        1      2       1+2 -> 3       # the 1 is from the previous result
3        3      3       3+3 -> 6

现在我们已经没有要处理的元素了,所以我们返回最终值6,这确实是1、2和3的总和。
让我们想象一个稍微复杂一些的场景——将一系列格式化的数字值添加到一个字符串中。这是reduce函数:
let sum = reduce(a,"") { (accumulator, nextValue) in accumulator + nextValue }

看起来几乎一样,但是你会注意到initial被设置为""而不是0。现在操作符+将一个字符串和一个整数组合。

call#    acc    next    result
1        ""      1      ""+1 -> "1"
2        "1"     2      "1"+2 -> "12"
3        "12"    3      "12"+3 -> "123"

我们完成了,可以打印字符串或其他内容。


现在让我们来看看你的第一个问题!

将一系列点添加到地图上类似于(但不完全相同于)将数字相加;它更接近于字符串示例,其中累加器具有与数组元素(整数/宝藏位置)不同的类型(字符串/地图)。

您确实可以将“城堡灰骷髅”添加到空地图中,然后将“阴暗海湾”添加到已绘制灰骷髅的地图上,最后将“宝藏位置”添加到已在其上绘制“城堡灰骷髅”和“阴暗海湾”的地图上。 然后您就完成了,可以开始寻宝之旅。

您的初始值不是零(或“”),而是空地图。

当添加新的点(矩形)时,它被添加到具有先前点的地图上,而不是直接添加到先前点上。

先前的点保持在地图上不变!它只是附近有了一个新的地图标记,可以分享故事。

至于您如何让大小为零的矩形做任何事情的问题,我认为这与一个点是一样的。


非常感谢您的详细回答。我仍然困惑的是,如何改变MKMapSize(width:0, height:0)的值? - user1107173
1
Alex是正确的 - 零宽度和高度的矩形本质上只是一个点(其原点)。调用MKMapRectUnion()时发生了变化,因为它返回包含两个参数的MKMapRect。即使这两个参数实际上只是点,联合体也将是包含两个点的矩形。 - Nate Cook
明白了。我刚查了一下MKMapRect,它返回一个原点和大小。我相信这会成为第二个调用的下一个值。 - user1107173
1
MKMapRect是一个带有originsize属性的值类型。return MKMapRectUnion(...)返回一个MKMapRect值(就像您简单示例中返回一个Int值一样),然后在下一次迭代中将其传回闭包。 - Nate Cook
1
第二次调用的下一个值是空地图,上面绘制了第一个地图点。 - Alex Brown

3
如果从命令式的角度来思考 reduce,那么你的代码基本上是在做这个事情:
var rectToDisplay = MKMapRectNull
for treasure in self.treasures {
    let treasurePointRect = MKMapRect(origin: treasure.location.mapPoint, size: MKMapSize(width: 0, height: 0))
    rectToDisplay = MKMapRectUnion(rectToDisplay, treasurePointRect)
}

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