Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laziness in Swift

Why is lazy used here?

extension SequenceType {
    func mapSome<U>(transform: Generator.Element -> U?) -> [U] {
        var result: [U] = []
        for case let x? in lazy(self).map(transform) {
            result.append(x)
        }
        return result
    }
}

this extension takes a transformation function that returns an optional, and returns an array of only those values that weren’t transformed into nil

Why not just use self.map(transform) ? is laziness necessary here?

like image 978
ielyamani Avatar asked Jul 05 '15 14:07

ielyamani


People also ask

What is lazy in Swift?

A lazy stored property is a property whose initial value isn't calculated until the first time it's used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

What is lazy and weak in Swift?

The error message is completely useless at explaining what is going on, but essentially lazy and weak are at odds with each other: lazy tells Swift that you don't want your variable created until the first time you access it, but once it is created, you want to keep it indefinitely for future reference, while.

How do you declare a lazy variable in Swift?

You cannot declare a lazy variable anywhere in the code. To make a variable lazy, use the keyword modifier lazy in front of var. Instead of directly assigning a value to the lazy variable, it has to be computed. The computation takes place in a block of code.

Is lazy thread safe in Swift?

Another problem is that lazy var is not thread-safe which means the closure can get executed multiple times due to accesses from different threads.


Video Answer


2 Answers

It avoids the creation of an intermediate array.

self.map(transform)

returns an array containing the results of the transformation of all sequence elements, which would then be traversed to build the resulting array with the non-nil elements.

lazy(self).map(transform)

is a sequence of the transformed elements, which is then iterated over to get the non-nil elements. The transformed elements are computed during the enumeration. (Each call to next() on the lazy sequence produces one element by transforming the next element of the original sequence.)

Both methods work. The lazy method would probably perform better for large sequences, but that can depend on many factors (the size of the array, whether the elements are value or reference types, how costly it is to copy array elements etc). For small arrays the lazy method would probably be slower due to the additional overhead. In a concrete application, profiling with Instruments would help to decide which method to use.

like image 51
Martin R Avatar answered Oct 17 '22 07:10

Martin R


As Martin R mentioned lazy() avoids the creation of an intermediate array. However if I compare the execution time of the function on arrays of different sizes you find that lazy() is "only" 10% faster.

Interestingly, you find that lazy() is for arrays with less than 200 elements up to 2 times as fast and gets with more elements almost equally fast as the function without the conversion (10% faster).

(Tested with Xcode 6.4 and Xcode 7 with global functions and protocol extension in a Playground as (compiled) source files)

So lazy() would rather be used for Sequences where you don't know if it is finite. Then, for loops are likely used with break or return:

for element in lazy(sequence).map{ ... } {
    if element == 1000 {
        break
    }
    // use element
}

If you call map on a infinite Sequence (like 1,2,3...) the execution would also be infinite. With lazy() the transformation and the execution get "delayed" thus you can handle "big" and infinite sequences more efficiently if you break out of the loop before the last element.

like image 38
Qbyte Avatar answered Oct 17 '22 07:10

Qbyte