ProgrammazioneSviluppatore iOS

Che cos'è una lazy sequence in Swift? Come funziona, quando dovrei usarla e quali sono le insidie?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Lazy sequence è un involucro speciale su una collezione che consente di ritardare l'esecuzione dei calcoli (filter, map, ecc.) fino al momento dell'accesso diretto agli elementi. Ciò significa che le operazioni su una lazy sequence verranno calcolate solo quando il loro risultato verrà richiesto (ad esempio, durante la chiamata a .forEach, .first, conversione in un array, ecc.).

Quando usare:

  • Quando si lavora con sequenze di grandi dimensioni, se non è necessario elaborare tutti gli elementi contemporaneamente.
  • Quando si desidera evitare allocazioni intermedie durante trasformazioni consecutive (map -> filter -> ...).

Esempio:

let numbers = Array(1...1_000_000) let lazyNumbers = numbers.lazy.map { $0 * 2 }.filter { $0 % 3 == 0 } let first = lazyNumbers.first // Solo adesso verrà calcolata la catena di operazioni per il primo elemento adatto!

Insidie:

  • I calcoli vengono eseguiti nuovamente ogni volta che si passa attraverso la .lazy sequence.
  • Dipendono dall'ordine delle chiamate: se è necessario attraversare i dati più volte con condizioni diverse, i dati verranno ricalcolati.
  • Le azioni ritardate possono essere fonte di effetti collaterali inaspettati se ci sono effetti collaterali nei closure annidati.

Domanda trabocchetto.

Qual è la differenza tra la chiamata .map { ... } e .lazy.map { ... } su un array?

Risposta:

  • .map { ... } applica il closure a ciascun elemento e restituisce immediatamente un nuovo array, quindi tutti gli elementi saranno elaborati e memorizzati in memoria.
  • .lazy.map { ... } restituisce non un array, ma una lazy-sequence (involucro), che non esegue l'elaborazione degli elementi immediatamente, ma solo quando vi si accede.

Esempio:

let a = Array(1...10) let eagers = a.map { $0 * 2 } // Array con 10 elementi let laziers = a.lazy.map { $0 * 2 } // LazySequence, che non contiene immediatamente risultati

Esempi di errori reali dovuti all'ignoranza delle sottigliezze dell'argomento.


Storia

In un grande progetto, uno sviluppatore ha applicato una catena di più chiamate map, filter, reduce a un enorme array di dati senza .lazy. Ciò ha portato all'allocazione temporanea di grandi array ad ogni passaggio intermedio, raddoppiando il consumo di memoria e causando crash su alcuni dispositivi con poca RAM.


Storia

In un blocco di codice è stata utilizzata una lazy sequence con un effetto collaterale nel closure interno (ad esempio, inviare un evento o stampare all'interno di map/filter). Lo sviluppatore si aspettava che questa operazione venisse eseguita immediatamente, tuttavia l'evento non è mai accaduto — perché gli elementi della lazy sequence non sono stati mai acceduti e il codice con l'evento non è stato mai eseguito. Di conseguenza, i log e le metriche sono risultati errati.


Storia

Nel caso della raccolta di statistiche dai dati di un grande database, è stata utilizzata una lazy sequence in combinazione con più passaggi (ad esempio, si cercava due volte il primo elemento, poi si contava). Ogni passaggio attraverso la lazy sequence ha innescato un completo ricalcolo delle operazioni — ciò ha portato a un rallentamento del doppio e a un carico non necessario sul sistema. Dopo la sostituzione con un array normale, il problema è scomparso.