ProgrammationDéveloppeur mobile

Comment fonctionne la fonction fonctionnelle reduce en Swift, quels en sont les avantages et les caractéristiques d'utilisation ? Quelles sont les erreurs et les pièges lors de l'application de reduce sur des collections ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Le mécanisme reduce fait partie des opérations fonctionnelles sur les collections de données et est venu en Swift des langages fonctionnels (map-reduce, fold). Historiquement, cette fonction permet de transformer n'importe quelle collection en une seule valeur agrégée (par exemple, la somme, le produit, la concaténation de chaînes, etc.), en parcourant tous ses éléments et en accumulant le résultat. Le problème de base qu'il résout est l'agrégation succincte, lisible et sans bogues des données au lieu de boucles manuelles.

En Swift, reduce est défini comme une méthode de collections :

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

Cela signifie que vous spécifiez une valeur initiale, puis vous écrivez une fonction qui renvoie une nouvelle valeur agrégée pour chaque élément et l'accumulateur actuel.

Exemple de code :

let numbers = [1, 2, 3, 4] let sum = numbers.reduce(0) { $0 + $1 } // 10 let joined = numbers.reduce("") { $0 + String($1) } // "1234"

Caractéristiques clés :

  • Permet d'écrire l'agrégation des collections en une seule ligne de code
  • Garantit l'absence d'effets secondaires — ne modifie pas les collections, fonctionne de manière fonctionnelle
  • Peut être utilisé pour n'importe quel type (pas seulement des nombres), y compris avec des exceptions (throws)

Questions piégeuses.

Comment fonctionne reduce sur une collection vide ?

Reduce est appliqué à chaque élément de la collection. Si la collection est vide, la valeur initiale est renvoyée, aucune invocation de closure ne sera faite.

Exemple de code :

let empty: [Int] = [] let sum = empty.reduce(100) { $0 + $1 } // 100

Peut-on modifier la collection d'origine avec reduce ?

Non, reduce est une fonction pure, elle ne modifie pas la collection d'origine. Tous les changements ne se produisent qu'avec l'accumulateur.

Quelle est la différence entre reduce et reduce(into:) ?

reduce(into:) permet de muter l'accumulateur à chaque passage et fonctionne plus efficacement avec les types value, où la création d'une nouvelle copie (copy-on-write) est coûteuse.

Exemple de code :

let nums = [1, 2, 3] let squares = nums.reduce(into: []) { (result: inout [Int], item) in result.append(item * item) } // squares == [1, 4, 9]

Erreurs typiques et anti-modèles

  • Mauvais choix de la valeur initiale (par exemple, lors de l'agrégation par multiplication — 0 au lieu de 1)
  • Effets secondaires à l'intérieur de la closure reduce (par exemple, modification de variables externes)
  • Utilisation de reduce pour des tâches mieux résolues par d'autres fonctions (par exemple, filter, map)

Exemple de la vie réelle

Cas négatif

Un développeur voulait sommer les prix des produits stockés dans un tableau products. Il a utilisé reduce avec une valeur initiale de 0.0, mais la closure était conçue de telle sorte qu'elle retournait parfois une somme négative en cas de réductions, ce qui a conduit à des résultats incorrects et à des problèmes de calcul de la taxe.

Avantages :

  • Code succinct
  • Pas de boucles manuelles

Inconvénients :

  • L'erreur n'a été remarquée qu'en production
  • Valeur commerciale incorrecte de la somme finale

Cas positif

Utilisation de reduce(into:) pour créer un cache à partir d'un tableau d'entités par id :

let objects: [Entity] = ... let cache = objects.reduce(into: [:]) { $0[$1.id] = $1 }

Avantages :

  • Rapide, efficace, pas de copie de tableau
  • Typage clair du cache

Inconvénients :

  • Le code reduce(into:) est un peu moins lisible pour les débutants
  • On peut accidentellement écraser des valeurs lors de clés en double