Pour garantir l’unicité des valeurs Swift synthétiques sans modifier leur ordre d’insertion, parcourez le tableau d’entrée une seule fois, suivez les clés acceptées dans un ensemble et ajoutez uniquement les premières occurrences à un tableau de sortie. Ce guide met en place un petit calcul local que vous pouvez exécuter sur un Mac.
La tâche consiste à traiter une liste ordonnée de valeurs de formes synthétiques, à supprimer les valeurs répétées et à calculer un total à partir des éléments conservés. Tout se passe en mémoire ; l’exemple ne lit aucune donnée externe et n’enregistre aucun résultat du calcul.
Définir ce que signifie « unique » avant de choisir une collection
Les tableaux Swift préservent l’ordre et autorisent les doublons. Les ensembles contiennent des valeurs uniques, mais leur ordre n’est pas défini. Par conséquent, convertir un tableau en ensemble, puis le reconvertir en tableau ne préserve pas l’ordre de la première occurrence. Un tri ultérieur établit un ordre de tri, qui peut différer de l’ordre d’insertion. Ces comportements des collections sont documentés dans les Types de collections de Swift.
Dans cet exemple, chaque valeur synthétique possède un nom de forme et une taille. Par défaut, deux éléments sont considérés comme des doublons uniquement si leurs deux propriétés correspondent. Un cercle de taille 12 et un cercle de taille 20 restent donc des valeurs distinctes.
| Question sur le contrat | Règle pour ce calcul |
|---|---|
| Qu’est-ce qui détermine l’ordre d’entrée ? | La position dans le tableau fourni |
| Qu’est-ce qui fait de deux valeurs des doublons ? | Une forme et une taille identiques |
| Quelle occurrence est conservée ? | La première occurrence |
| Un doublon ultérieur déplace-t-il la valeur conservée ? | Non |
| Que se passe-t-il avec une entrée vide ? | Renvoyer un tableau vide et un total de zéro |
| Quelles valeurs contribuent au total ? | Uniquement les valeurs conservées |
Ce contrat évite une erreur courante : supprimer des éléments selon le nom de leur forme alors que le calcul doit distinguer les combinaisons forme-taille. L’exemple détaillé calcule aussi le résultat fondé uniquement sur la forme, afin que vous puissiez constater les conséquences d’un changement de règle.
Créer un exemple Swift local complet sur votre Mac
Vous devez disposer d’une chaîne d’outils Swift installée. Dans Terminal, exécutez swift --version pour vérifier que la commande est disponible. Le tutoriel officiel de Swift Créer un outil en ligne de commande documente cette vérification, la création d’un paquet exécutable et son exécution.
Créez un répertoire pour l’exemple et initialisez un paquet exécutable :
mkdir OrderDemo
cd OrderDemo
swift package init --name OrderDemo --type executable
Remplacez le fichier source exécutable généré dans Sources par le programme complet ci-dessous. Le nom du fichier généré et la structure des répertoires peuvent varier selon la chaîne d’outils ; remplacez son point d’entrée existant au lieu d’en ajouter un second. Ce programme utilise un point d’entrée @main : placez-le donc dans un fichier nommé OrderDemo.swift, en renommant un fichier généré main.swift si nécessaire. Le tutoriel en ligne de commande de Swift explique le choix entre un fichier main.swift et un point d’entrée @main.
Aucun import ni aucune dépendance de paquet ne sont nécessaires. Le programme utilise des types et des fonctions de la bibliothèque standard.
struct Tile: Hashable {
let shape: String
let size: Int
}
func uniqueFirst<Value, Key: Hashable>(
_ values: [Value],
by key: (Value) -> Key
) -> [Value] {
var seen: Set<Key> = []
var result: [Value] = []
for value in values {
if seen.insert(key(value)).inserted {
result.append(value)
}
}
return result
}
@main
struct OrderDemo {
static func main() {
let input = [
Tile(shape: "circle", size: 12),
Tile(shape: "square", size: 8),
Tile(shape: "circle", size: 12),
Tile(shape: "circle", size: 20),
Tile(shape: "square", size: 8)
]
let retained = uniqueFirst(input, by: { $0 })
let total = retained.reduce(0) { $0 + $1.size }
let skipped = input.count - retained.count
let expected = [
Tile(shape: "circle", size: 12),
Tile(shape: "square", size: 8),
Tile(shape: "circle", size: 20)
]
precondition(retained == expected)
precondition(total == 40)
precondition(skipped == 2)
let empty: [Tile] = []
precondition(uniqueFirst(empty, by: { $0 }).isEmpty)
precondition(
uniqueFirst([input[0], input[0]], by: { $0 })
== [input[0]]
)
let byShape = uniqueFirst(input, by: { $0.shape })
precondition(byShape == [input[0], input[1]])
let shapeTotal = byShape.reduce(0) { $0 + $1.size }
precondition(shapeTotal == 20)
for tile in retained {
print("\(tile.shape): \(tile.size)")
}
print("Total: \(total)")
print("Skipped duplicates: \(skipped)")
print("Shape-only total: \(shapeTotal)")
}
}
Depuis le répertoire du paquet, exécutez :
swift run OrderDemo
La sortie attendue du programme, après les éventuels messages de compilation, est présentée ci-dessous. Le programme a été vérifié au regard de la documentation d’API récupérée, mais il n’a pas été exécuté ici, car cet environnement de recherche ne dispose pas d’un exécutable Swift.
circle: 12
square: 8
circle: 20
Total: 40
Skipped duplicates: 2
Shape-only total: 20
Pourquoi l’approche avec un tableau et un ensemble préserve-t-elle l’ordre ?
Le tableau est l’unique source de l’ordre de sortie. La boucle parcourt chaque position d’entrée dans l’ordre, et append(_:) place une valeur acceptée à la fin du résultat. L’ensemble sert uniquement à vérifier si la clé choisie est déjà apparue ; le programme ne le parcourt jamais.
L’expression essentielle est seen.insert(key(value)).inserted. Swift indique que insert(_:) renvoie inserted sous la forme true pour un nouvel élément et false lorsqu’un élément égal existe déjà. L’élément existant reste inchangé. Cette valeur de retour permet à une seule opération de décider s’il faut ajouter la valeur d’origine. Consultez la documentation d’insertion dans un ensemble d’Apple.
Pour l’entrée fournie, le premier cercle et le carré sont acceptés. Le deuxième cercle identique est ignoré. Le cercle plus grand est accepté, car sa valeur complète diffère, et le carré répété est ignoré. Les tailles conservées sont donc 12 + 8 + 20 = 40.
L’implémentation maintient un invariant utile : après chaque itération, le résultat contient exactement la valeur rencontrée en premier pour chaque clé de seen, dans l’ordre de rencontre. Une nouvelle clé étend les deux collections ; une clé répétée ne modifie ni l’une ni l’autre. Ce raisonnement explique la garantie d’ordre indépendamment de la façon dont l’ensemble stocke ses éléments.
Comment les valeurs Swift personnalisées doivent-elles exprimer l’égalité ?
La déclaration struct Tile: Hashable demande une conformité synthétisée. Les deux propriétés stockées, String et Int, sont hachables, ce qui permet à Swift de synthétiser le comportement requis. Apple documente ces conditions de synthèse dans Hashable.
Utiliser { $0 } comme clé signifie que la valeur Tile entière détermine l’unicité. Utiliser { $0.shape } modifie le contrat : une seule entrée est conservée par forme. Dans l’exemple, cela supprime le cercle ultérieur de taille 20, ce qui ramène le total à 20. Il s’agit d’une comparaison délibérée entre deux règles, et non d’une seconde implémentation de la même règle.
Veillez à ce que le choix de la clé soit déterministe pendant toute la durée du calcul. Dérivez-la de la valeur traitée plutôt que d’un compteur ou d’une variable externe susceptible de changer. Sinon, des entrées égales pourraient recevoir des clés différentes et être conservées comme des éléments distincts.
Si vous implémentez manuellement l’égalité et le hachage, utilisez les mêmes propriétés essentielles pour les deux. Apple exige que des instances égales fournissent au hacheur les mêmes valeurs dans le même ordre. Évitez d’utiliser hashValue lui-même comme clé d’unicité : un hachage ne remplace pas le contrat d’égalité de la valeur. Conserver la véritable clé hachable permet à l’ensemble d’appliquer ce contrat. Consultez les exigences de conformité de Hashable.
Quand faut-il plutôt utiliser une collection ordonnée ?
Pour ce calcul unique, un tableau associé à un ensemble local rend le comportement explicite. Si une application ajoute, supprime, réordonne et recherche régulièrement des éléments uniques, une collection ordonnée dédiée peut faciliter la maintenance de ces opérations.
| Choix | Cas adapté | Limite à prendre en compte |
|---|---|---|
| Tableau et ensemble | Un seul parcours de valeurs Swift synthétiques, éventuellement avec une clé sélectionnée | Votre fonction définit le comportement de la première occurrence |
OrderedSet de Swift Collections |
Utilisation régulière d’une collection ordonnée de valeurs hachables uniques | Nécessite le paquet Swift Collections séparé |
NSOrderedSet de Foundation |
Une API utilise déjà des collections ordonnées d’objets de Foundation | Classe représentant une collection ordonnée statique ; les opérations de modification utilisent NSMutableOrderedSet |
La documentation officielle de Swift Collections décrit OrderedSet comme une collection ordonnée d’éléments uniques, importée via OrderedCollections. Elle fournit des opérations tenant compte des positions, comme append(_:) et insert(_:at:). Ce sont des API de paquet : ajouter uniquement import OrderedCollections ne configure pas un projet qui n’a aucune dépendance. Consultez OrderedSet.
La documentation Foundation d’Apple décrit NSOrderedSet comme une collection ordonnée statique d’objets distincts et désigne NSMutableOrderedSet comme son pendant modifiable. Pour le calcul de valeurs Swift natives présenté ci-dessus, la fonction générique exprime directement la clé d’égalité choisie sans introduire cette collection d’objets. Consultez NSOrderedSet.
Qu’est-ce qui change lorsque le calcul alimente une interface de bureau ?
L’exemple complet est synchrone et gère ses collections temporaires dans la fonction. Pour une petite liste synthétique, cela établit un ordre de traitement clair sans introduire de tâches.
Dans une interface macOS, isolez correctement l’état et les mises à jour liés à l’interface. Apple décrit MainActor comme un acteur global dont l’exécuteur équivaut à la file d’attente de distribution principale ; le guide de concurrence de Swift explique comment appliquer @MainActor aux fonctions et aux types qui accèdent à l’état de l’interface. Consultez MainActor et Concurrence.
La concurrence exige aussi une définition explicite de l’ordre. Swift ne garantit pas l’ordre d’achèvement des tâches enfants dans un groupe de tâches. Ajouter les résultats à mesure que les tâches se terminent préserve donc l’ordre d’achèvement, qui peut différer de l’ordre d’origine des entrées. Le guide officiel sur la concurrence documente cette limite.
Si une version future exécute en parallèle des calculs synthétiques indépendants, associez chaque position d’entrée à son résultat, rétablissez l’ordre des positions d’entrée après la collecte, puis appliquez le parcours d’unicité. Cette conception découle de la limite documentée concernant l’ordre d’achèvement. Gardez l’exemple actuel synchrone tant que des opérations supplémentaires ne justifient pas cette coordination.
Vérifier le contrat avant de réutiliser la fonction
Le programme comprend des vérifications portant sur l’ordre exact des valeurs conservées, l’entrée vide, les valeurs identiques répétées et la règle alternative fondée uniquement sur la forme. Ces vérifications ciblent les décisions les plus susceptibles de modifier le sens du calcul.
Lors de l’adaptation, vérifiez trois conditions : l’entrée doit déjà avoir un ordre significatif, la clé doit exprimer la règle d’égalité voulue et il doit être approprié de conserver la première occurrence. La fonction ignore délibérément les éléments correspondants ultérieurs ; elle ne fusionne pas leurs tailles et ne remplace pas la valeur conservée. Le nombre de doublons ignorés rend cet effet visible.
Pour la liste de formes synthétiques fournie, le résultat final contient trois valeurs dans l’ordre de leur première occurrence et un total de 40. Confiez l’ordre au tableau, l’appartenance à l’ensemble et la définition de « unique » à la clé choisie.
Sources
- Types de collections — The Swift Programming Language, projet Swift.
- Créer un outil en ligne de commande — Swift.org.
- insert(_:) — Documentation Apple pour les développeurs.
- Hashable — Documentation Apple pour les développeurs.
- OrderedSet — Apple, Swift Collections.
- NSOrderedSet — Documentation Apple pour les développeurs.
- MainActor — Documentation Apple pour les développeurs.
- Concurrency — The Swift Programming Language, projet Swift.