Swift: shrink a function with closure

Below is the code I am trying to understand:

  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)
    }

      

My understanding of the reduction function:

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

      

My understanding of closure:

{ (params) -> returnType in
  statements
}

      

My understanding of the code from above:

// 1 = initial value for the decrease function is set to(MKMapRectNull)

// 2 = Instead of the current amount and object in the array, the closure is passed with two arguments, which return MKMapRect

:

(mapRect: MKMapRect, treasure: Treasure) -> MKMapRect 

      

// 3 = This is where I got stuck. The structure MKMapRect

is called with two parameters origin: treasure.location.mapPoint

andsize: MKMapSize(width: 0, height: 0)

Question 1: . How will you MKMapSize

calculate if the values ​​have passed 0,0

? How do I get subsequent values ​​and add them?

Question 2: When this string is returned in //2 closure

return MKMapRectUnion(mapRect, treasurePointRect)

, how does it become the current amount and how does it know to move to the next element of self.treasures?

+3


source to share


2 answers


Answer 2:

The first argument of the second (third, fourth) call to the closure is the result of the previous call to the closure. The only exception is the first call, which has no previous call to inherit, so the abbreviation takes a value of 0 as the second argument - this is a special value to feed into the first call.

Imagine the following scenario, you have an array of numbers:

let a = [1,2,3]

      

And you want to find the sum using abbreviation:

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

      

So let's see what happens on each call:

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

      

We now run out of items to process, so we return the final value 6, which is indeed the sum of 1,2 and 3


Imagine a more complex scenario - adding a series of formatted numbers to a string. Here's an abbreviation:

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

      



It looks almost identical, but you will notice that the initial value is set to a value ""

instead of 0. The + operator now concatenates String and Int.

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

      

And we are done, and we can print a string or whatever.


Now let's look at our first question!

Adding a series of points to a map is similar (but not quite the same as) adding numbers together; it is closer to the string example where the accumulator has a different type (string / map) from array elements (integer / treasure).

You can add a "gray gray skull" to an empty map, then add a "shady cove" to a map with a gray skull on it, and then add a "treasure location" to a map with a "gray castle skull" and a "shady cove" already drawn on German Then you are done and you can start your treasure hunt.

Your initial value is not zero (or ""), but an empty card.

When a new point (rect) is added, it is added to the map with the previous point on it, it is not added directly to the previous point.

The previous point remains unchanged on the map! he just has a new map marker to share stories.

As for your question about how a zero-sized rectangle can do anything, I assume it is exactly the same as a point.

+7


source


If it helps to think about reduce

from an imperative perspective, this is basically what your code does:



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)
}

      

+3


source







All Articles