Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why and when to use lazy with Array in Swift?

[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3  [1, 2, 3, -1, -2].lazy.filter({ $0 > 0 }).count // => 3 

What is the advantage of adding lazy to the second statement. As per my understanding, when lazy variable is used, memory is initialized to that variable at the time when it used. How does it make sense in this context?

enter image description here

Trying to understand the the use of LazySequence in little more detail. I had used the map, reduce and filter functions on sequences, but never on lazy sequence. Need to understand why to use this?

like image 440
Deep Arora Avatar asked Aug 19 '18 11:08

Deep Arora


People also ask

What is a lazy array?

A Lazy array is an array that is stored in memory as an expression or function (same difference, only the type is different). It might be helpful to get an example to visualize what exactly that means. Consider the following array: x = [5, 10, 15, 20]

What is lazy map in Swift?

Lazy collections are similar to a regular collection but change the way how modifiers like map , filter , and reduce are processed. In my experience, they haven't got as much attention as they should as they can be more performant in certain cases.

What is lazy Swift?

Swift has a mechanism built right into the language that enables just-in-time calculation of expensive work, and it is called a lazy variable. These variables are created using a function you specify only when that variable is first requested.


1 Answers

lazy changes the way the array is processed. When lazy is not used, filter processes the entire array and stores the results into a new array. When lazy is used, the values in the sequence or collection are produced on demand from the downstream functions. The values are not stored in an array; they are just produced when needed.

Consider this modified example in which I've used reduce instead of count so that we can print out what is happening:

Not using lazy:

In this case, all items will be filtered first before anything is counted.

[1, 2, 3, -1, -2].filter({ print("filtered one"); return $0 > 0 })     .reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 } 
filtered one filtered one filtered one filtered one filtered one counted one counted one counted one 

Using lazy:

In this case, reduce is asking for an item to count, and filter will work until it finds one, then reduce will ask for another and filter will work until it finds another.

[1, 2, 3, -1, -2].lazy.filter({ print("filtered one"); return $0 > 0 })     .reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 } 
filtered one counted one filtered one counted one filtered one counted one filtered one filtered one 

When to use lazy:

option-clicking on lazy gives this explanation:

pop-up for lazy in Xcode

From the Discussion for lazy:

Use the lazy property when chaining operations:

  1. to prevent intermediate operations from allocating storage

    or

  2. when you only need a part of the final collection to avoid unnecessary computation

    I would add a third:

  3. when you want the downstream processes to get started sooner and not have to wait for the upstream processes to do all of their work first

So, for example, you'd want to use lazy before filter if you were searching for the first positive Int, because the search would stop as soon as you found one and it would save filter from having to filter the whole array and it would save having to allocate space for the filtered array.

For the 3rd point, imagine you have a program that is displaying prime numbers in the range 1...10_000_000 using filter on that range. You would rather show the primes as you found them than having to wait to compute them all before showing anything.

like image 75
vacawama Avatar answered Oct 15 '22 05:10

vacawama