ProgrammationDéveloppeur iOS

Qu'est-ce qu'une séquence paresseuse en Swift ? Comment fonctionne-t-elle, quand faut-il l'utiliser et quels en sont les pièges ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Lazy sequence est un wrapper spécial autour d'une collection qui permet de reporter l'exécution des calculs (filter, map, etc.) jusqu'à l'accès direct aux éléments. Cela signifie que les opérations sur lazy sequence ne seront calculées que lorsqu'on y accède (par exemple, lors d'un appel à .forEach, .first, conversion en tableau, etc.).

Quand utiliser :

  • Lorsqu'on travaille avec de grandes séquences, s'il n'est pas nécessaire de traiter tous les éléments immédiatement.
  • Lorsque nous voulons éviter les allocations intermédiaires lors des transformations successives (map -> filter -> ...).

Exemple :

let numbers = Array(1...1_000_000) let lazyNumbers = numbers.lazy.map { $0 * 2 }.filter { $0 % 3 == 0 } let first = lazyNumbers.first // La chaîne d'opérations ne sera calculée que maintenant pour le premier élément correspondant !

Pièges :

  • Les calculs se reproduisent à chaque itération de la .lazy sequence.
  • Dépendent de l'ordre des appels : si vous devez parcourir plusieurs fois les données avec différentes conditions — les données seront recalculées.
  • Les actions retardées peuvent être source d'effets secondaires inattendus, si des effets secondaires existent dans les closures imbriquées.

Question piège.

Quelle est la différence entre l'appel de .map { ... } et .lazy.map { ... } sur un tableau ?

Réponse :

  • .map { ... } applique la closure à chaque élément et retourne immédiatement un nouveau tableau, c’est-à-dire que tous les éléments seront traités et enregistrés en mémoire.
  • .lazy.map { ... } ne retourne pas un tableau, mais une lazy-sequence (wrapper), qui ne traite pas les éléments immédiatement, mais uniquement lors de l'accès à ceux-ci.

Exemple :

let a = Array(1...10) let eagers = a.map { $0 * 2 } // Tableau avec 10 éléments let laziers = a.lazy.map { $0 * 2 } // LazySequence, ne contenant pas immédiatement les résultats

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet.


Histoire

Dans un grand projet, un développeur a appliqué une chaîne de plusieurs appels map, filter, reduce à un énorme tableau de données sans .lazy. Cela a entraîné une allocation temporaire de grands tableaux à chaque étape intermédiaire, doublant quasiment l'utilisation de la mémoire et provoquant des crashs sur certains appareils avec une mémoire RAM basse.


Histoire

Dans un bloc de code, une lazy sequence a été utilisée avec un effet secondaire dans une closure interne (par exemple, envoi d'un événement ou impression à l'intérieur de map/filter). Le développeur s'attendait à ce que cette opération se produise immédiatement, cependant, l'événement ne s'est jamais produit — parce que les éléments de la lazy sequence n'avaient jamais été accédés, et le code avec l'événement n'a pas été appelé du tout. En conséquence, les journaux et les métriques se sont révélés incorrects.


Histoire

Dans le cadre de la collecte de statistiques sur les données d'une grande base, une lazy sequence a été utilisée avec plusieurs passages (par exemple, à deux reprises pour chercher le premier, puis pour compter). Chaque passage sur la lazy sequence a déclenché un recalcul complet des opérations — ce qui a entraîné un ralentissement par deux et une charge inutile sur le système. Après remplacement par un tableau ordinaire, le problème a disparu.