Onderdruk opeenvolgende Swift-enumgevallen terwijl je geassocieerde waarden negeert door een afzonderlijke gevalssleutel te vergelijken en de eerste volledige waarde in elke reeks te behouden. Dit lokale Mac-voorbeeld behoudt de volgorde en laat een geval opnieuw voorkomen na een ander geval.
Bepaal precies wat als duplicaat geldt
Stel dat een grafische desktopvoorbeeldweergave een synthetische reeks stappen oplevert: inactief zijn, een benoemde vorm tekenen en een benoemde voorvertoning tonen. Je wilt een compacte lijst met gevalsovergangen. Twee opeenvolgende tekenstappen tellen als duplicaten, zelfs wanneer de namen van de vormen verschillen.
Swift-enums kunnen verschillende geassocieerde waarden bevatten voor elk geval. Die waarden maken deel uit van de enumwaarde, dus als je ze wilt negeren, heb je een expliciete vergelijkingsregel nodig. De Swift-taalgids legt dit onderscheid uit in Enumerations.
Gebruik voor deze taak het volgende contract:
- Behoud de eerste invoerwaarde.
- Onderdruk elke latere waarde waarvan het geval overeenkomt met dat van de voorafgaande reeks.
- Behoud de volledige geassocieerde waarde van elk behouden element.
- Behoud een geval opnieuw wanneer het volgt op een ander geval.
- Behoud de invoervolgorde; lege invoer levert een leeg resultaat op.
Hieronder zie je het beoogde gedrag voor een illustratieve invoer. Alle namen zijn synthetisch.
| Invoerwaarde | Beslissing | Reden |
|---|---|---|
.idle |
Behouden | Eerste waarde |
.drawing("Circle") |
Behouden | Geval gewijzigd |
.drawing("Square") |
Onderdrukken | Nog steeds aan het tekenen |
.ready("Preview A") |
Behouden | Geval gewijzigd |
.ready("Preview B") |
Onderdrukken | Nog steeds gereed |
.drawing("Triangle") |
Behouden | Tekenen volgt op gereed |
.idle |
Behouden | Geval gewijzigd |
.idle |
Onderdrukken | Nog steeds inactief |
De behouden tekenwaarden zijn dus "Circle" en "Triangle". De payload "Square" wordt bewust uitgesloten. Als de desktopweergave elke bijgewerkte vormnaam moet tonen, zou dit contract wijzigingen weggooien die die weergave nodig heeft; kies voor die toepassing een payloadgevoelige vergelijking.
Voer een volledig lokaal Swift-voorbeeld uit op je Mac
Gebruik een Mac met een werkende Swift-ontwikkelomgeving. Swift.org beveelt Xcode aan voor ontwikkeling op Apple-platforms en documenteert een workflow voor uitvoerbare programma's via de opdrachtregel met Swift Package Manager. Volg Install Swift — macOS als je de hulpmiddelen nog moet installeren.
Maak in Terminal een klein uitvoerbaar pakket aan in een nieuwe werkmap:
mkdir EnumCaseDemo
cd EnumCaseDemo
swift package init --name EnumCaseDemo --type executable
Vervang de inhoud van het gegenereerde bronbestand van het uitvoerbare programma door het volgende volledige programma. Het programma gebruikt typen uit de Swift-standaardbibliotheek en vereist geen imports of pakketafhankelijkheden. De invoer en uitvoer blijven in het geheugen; het programma drukt de behouden synthetische stappen af.
enum PreviewStep: Equatable {
case idle
case drawing(String)
case ready(String)
enum Kind: Equatable {
case idle
case drawing
case ready
}
var kind: Kind {
switch self {
case .idle:
return .idle
case .drawing(_):
return .drawing
case .ready(_):
return .ready
}
}
var label: String {
switch self {
case .idle:
return "idle"
case .drawing(let shape):
return "drawing: \(shape)"
case .ready(let name):
return "ready: \(name)"
}
}
}
func suppressConsecutiveCases(
_ steps: [PreviewStep]
) -> [PreviewStep] {
var result: [PreviewStep] = []
var previousKind: PreviewStep.Kind? = nil
for step in steps {
let currentKind = step.kind
if let previous = previousKind,
previous == currentKind {
continue
}
result.append(step)
previousKind = currentKind
}
return result
}
let input: [PreviewStep] = [
.idle,
.drawing("Circle"),
.drawing("Square"),
.ready("Preview A"),
.ready("Preview B"),
.drawing("Triangle"),
.idle,
.idle
]
let expected: [PreviewStep] = [
.idle,
.drawing("Circle"),
.ready("Preview A"),
.drawing("Triangle"),
.idle
]
let output = suppressConsecutiveCases(input)
precondition(output == expected)
precondition(suppressConsecutiveCases([]).isEmpty)
precondition(
suppressConsecutiveCases([
.drawing("Circle"), .drawing("Square")
]) == [.drawing("Circle")]
)
precondition(
suppressConsecutiveCases([
.idle, .drawing("Circle"), .idle
]) == [.idle, .drawing("Circle"), .idle]
)
for step in output {
print(step.label)
}
Voer het pakket uit vanuit de map:
swift run EnumCaseDemo
De verwachte uitvoer van het programma, na eventuele bouwmeldingen, is:
idle
drawing: Circle
ready: Preview A
drawing: Triangle
idle
Deze voorbeelden zijn gecontroleerd aan de hand van de opgehaalde documentatie; ze zijn hier niet uitgevoerd omdat de onderzoeksomgeving geen Swift-uitvoerbaar bestand bevat. De opgenomen precondities controleren de verwachte behouden payloads, lege invoer, opeenvolgende verschillende payloads en een geval dat na een overgang terugkeert.
Waarom een afzonderlijke gevalssleutel werkt
De eigenschap kind zet elke volledige enumwaarde om in een kleine sleutel zonder payload. Zowel .drawing("Circle") als .drawing("Square") levert de sleutel .drawing op. De filterfunctie vergelijkt deze sleutels terwijl hij de oorspronkelijke step toevoegt, zodat de behouden geassocieerde waarden intact blijven.
De functie hoeft maar één sleutel te onthouden. Aan het begin is previousKind gelijk aan nil, waardoor het eerste element wordt doorgelaten. Na het behouden van een element onthoudt de functie het type van dat element. Onderdrukte elementen hebben hetzelfde type, dus de laatst onthouden sleutel voor die elementen bijwerken zou niets veranderen.
Deze implementatie doorloopt de invoer één keer. Voor deze vaste enum en de sleutels met constante grootte voert de functie per volgend element één vergelijking van gevalssleutels uit en gebruikt ze een constante hoeveelheid extra status, plus de geretourneerde array. De documentatie van Swift over Sequence beschrijft sequentiële iteratie en het gebruikelijke lineaire doorloopmodel.
De volledige PreviewStep voldoet ook aan Equatable, zodat de precondities volledige resultaten kunnen controleren, inclusief behouden strings. Swift ondersteunt automatisch gegenereerde gelijkheid voor geschikte enums waarvan de geassocieerde waarden voldoen aan Equatable; zie Protocols. Door de vergelijking van gevallen in Kind onder te brengen, krijgt deze filterbewerking een eigen expliciete regel zonder de gelijkheid in het hele programma opnieuw te definiëren.
De exhaustieve switch in kind biedt ook een nuttig onderhoudsmoment. Voeg bij een nieuw geval de bijbehorende sleutel en mapping toe. Vermijd een mapping die alles opvangt, zodat een nieuw geval niet per ongeluk bij een bestaand geval wordt ingedeeld. Swift documenteert exhaustieve enum-switches in Enumerations.
Gebruik dezelfde vergelijking met Combine
Als je Mac-code al een Combine-publisher gebruikt, biedt Apple removeDuplicates(by:) voor een aangepaste vergelijking. De predicate retourneert true wanneer de tweede waarde als duplicaat moet tellen. Apple ondersteunt deze operator uitdrukkelijk wanneer de gewenste vergelijking afwijkt van de gewone gelijkheid van het uitvoertype. De operator is beschikbaar in macOS 10.15 en hoger. Zie removeDuplicates(by:).
Het volgende is een fragment voor een extensie, geen zelfstandig programma. Voeg het toe aan het volledige voorbeeld hierboven op een Mac waarop Combine beschikbaar is. Het hergebruikt de bestaande definitie van PreviewStep en de array input:
import Combine
let subscription = input.publisher
.removeDuplicates { previous, current in
previous.kind == current.kind
}
.sink { step in
print("Combine: \(step.label)")
}
Dit drukt dezelfde vijf behouden stappen af, met het voorvoegsel "Combine: ". De predicate vergelijkt gevalssleutels en de publisher geeft de oorspronkelijke behouden enumwaarden door. Het eigen voorbeeld van Apple voor deze operator volgt hetzelfde principe door één eigenschap te vergelijken en een andere te negeren. removeDuplicates(by:)
Houd het geretourneerde abonnement in leven zolang het abonnement nodig is. Apple documenteert dat het vrijgeven van de waarde die door sink(receiveValue:) wordt geretourneerd, het abonnement beëindigt. Deze overload geldt voor publishers waarvan het fouttype Never is, zoals in de voorbeelden van Apple met sequence-publishers. sink(receiveValue:)
Controleer de grenzen voordat je de code aanpast
Gebruik de arrayfunctie wanneer je al een eindige lokale lijst hebt. Gebruik de Combine-operator wanneer de waarden al via een publisher binnenkomen. Beide versies drukken hier hetzelfde contract voor gevalsovergangen uit.
| Vereiste | Geschikte vergelijking of aanpak |
|---|---|
| De eerste volledige waarde in elke gevalsreeks behouden | Vergelijk kind, zoals getoond |
| Wijzigingen in geassocieerde strings behouden | Vergelijk volledige waarden met gewone gelijkheid |
| De laatste volledige waarde in elke reeks behouden | Bewaar een waarde in afwachting en verwerk die bij een gevalsovergang en aan het einde |
| Een terugkerend geval behouden na een ander geval | Vergelijk opeenvolgende reeksen in plaats van een globale lijst met reeds geziene gevallen bij te houden |
| Afzonderlijke arrays onafhankelijk filteren | Roep de functie afzonderlijk aan; elke aanroep begint met een lege vergelijkingsstatus |
De laatste payload behouden vereist een ander algoritme, omdat dit programma de eerste waarde meteen toevoegt. Ook wordt de vergelijkingsstatus gereset wanneer je één logische reeks opsplitst in afzonderlijke arrays: een tekenwaarde aan het einde van de ene batch en een andere aan het begin van de volgende blijven allebei behouden bij afzonderlijke aanroepen. Als batches zich als één reeks moeten gedragen, behoud dan de vorige sleutel over batchgrenzen heen.
Foundation biedt collecties en andere basisfunctionaliteit voor apps op Apple-platforms, maar deze berekening wordt volledig met Swift-typen uitgevoerd. De publisher-variant vereist specifiek Combine; import Foundation voegt die operator niet toe. Apple documenteert deze frameworks afzonderlijk in Foundation en removeDuplicates(by:).
De synchrone arrayfunctie heeft ook geen gelijktijdigheidsmechanismen nodig: de vergelijkingsstatus hoort bij elke aanroep. Als je later gedeelde status voor de desktopweergave introduceert, bepaal dan afzonderlijk hoe die status wordt geïsoleerd. Apple documenteert MainActor als een singleton-actor waarvan de uitvoerder gelijkwaardig is aan de hoofd-dispatchwachtrij; het onderdrukken van duplicaten kiest zelf geen actor. MainActor
Voor de lokale taak die hier wordt getoond, is de doorslaggevende keuze welke payload een reeks overleeft. Zodra je de eerste payload kiest, maakt een afzonderlijke gevalssleutel het gedrag eenvoudig te lezen en te controleren: behoud de oorspronkelijke waarde bij een gevalsovergang, onderdruk overeenkomende opeenvolgende sleutels en laat terugkerende gevallen door.
Bronnen
- Enumerations — The Swift Programming Language, Swift.org.
- Protocols — The Swift Programming Language, Swift.org.
- Install Swift — macOS — Swift.org.
- Sequence — Apple Developer Documentation.
- removeDuplicates(by:) — Apple Developer Documentation.
- sink(receiveValue:) — Apple Developer Documentation.
- Foundation — Apple Developer Documentation.
- MainActor — Apple Developer Documentation.