Schrijf een functie die voor elk veld de bron kiest en een nieuwe waarde retourneert om twee Swift-structwaarden samen te voegen. Dit macOS-voorbeeld combineert twee synthetische stijlen voor tekenpanelen, met expliciete regels voor vervangen, behouden, optioneel wissen en ongeldige invoer.
Bepaal wat voorrang heeft voordat je de samenvoeging schrijft
De taak van de lezer is om op een Mac één stijl voor een tekenpaneel te berekenen op basis van twee bestaande waarden. Noem de invoerwaarden base en incoming: deze namen beschrijven hun rol, terwijl een contract per veld bepaalt welke waarde voorrang heeft. De berekening gebruikt uitsluitend synthetische waarden in het geheugen.
Swift beschrijft structuren als waardetypen en ondersteunt het aanmaken van instanties via initializers. Apple's gedocumenteerde API merge(_:uniquingKeysWith:) hoort bij Dictionary; deze lost dubbele sleutels op met een closure. Definieer voor deze aangepaste struct een samenvoegfunctie waarvan de implementatie de gewenste veldregels uitdrukt. Structures and Classes, Dictionary merge(_:uniquingKeysWith:).
Gebruik het volgende illustratieve contract. Dit zijn ontwerpkeuzes voor dit voorbeeld, geen gedrag dat Swift automatisch biedt.
| Veld | Regel | Gevolg in het voorbeeld |
|---|---|---|
title |
Neem de binnenkomende tekst, verwijder omringende witruimte en regeleinden en wijs daarna een leeg resultaat af | " Tile Study \n" wordt \"Tile Study\" |
spacing |
Behoud de basiswaarde; deze moet niet-negatief zijn | De basiswaarde 12 krijgt voorrang op de binnenkomende waarde 30 |
showsGrid |
Neem de binnenkomende Boolean exact over | De binnenkomende waarde false vervangt de basiswaarde true |
caption |
Neem de binnenkomende optionele waarde exact over | De binnenkomende waarde nil wist het bijschrift van de basiswaarde |
Deze tabel maakt onderscheid tussen drie handelingen die gemakkelijk door elkaar worden gehaald: een bron kiezen, de gekozen waarde normaliseren en het resultaat valideren. De binnenkomende spatiëring wordt bewust niet gebruikt. Die kan de uitvoer niet veranderen, ook niet als de waarde afwijkt van de basisspatiëring.
Foundation's trimmingCharacters(in:) retourneert een nieuwe tekenreeks waarin overeenkomende tekens aan beide uiteinden zijn verwijderd. Apple documenteert het gebruik van .whitespacesAndNewlines voor omringende witruimte; een tekenreeks die geheel uit zulke tekens bestaat, wordt leeg. Het afwijzen van dat lege resultaat is onze aanvullende regel. Foundation trimmingCharacters(in:).
Voer een compleet lokaal voorbeeld uit op je Mac
Gebruik voor dit voorbeeld een Swift 6-toolchain. Swift.org biedt macOS-installatie-instructies via Swiftly en noemt Xcode als optie voor ontwikkeling op Apple-platforms. Open Terminal en voer swift --version uit om te controleren of de opdracht beschikbaar is. Als die ontbreekt, volg dan eerst de installatie-instructies. Install Swift on macOS, Getting Started.
Sla het volgende volledige programma op als MergeStyles.swift in een werkmap. Het importeert Foundation, definieert beide invoerwaarden, voert de samenvoeging uit, controleert het verwachte resultaat en test de twee afwijzingsgevallen. De berekening doet geen netwerkaanvragen en slaat geen toepassingsgegevens op.
import Foundation
struct PanelStyle: Equatable, Sendable {
let title: String
let spacing: Int
let showsGrid: Bool
let caption: String?
}
enum StyleMergeError: Error {
case emptyTitle
case negativeSpacing
}
func mergeStyles(
base: PanelStyle,
incoming: PanelStyle
) throws -> PanelStyle {
let title = incoming.title.trimmingCharacters(
in: .whitespacesAndNewlines
)
guard !title.isEmpty else {
throw StyleMergeError.emptyTitle
}
guard base.spacing >= 0 else {
throw StyleMergeError.negativeSpacing
}
return PanelStyle(
title: title,
spacing: base.spacing,
showsGrid: incoming.showsGrid,
caption: incoming.caption
)
}
let base = PanelStyle(
title: "Shape Study",
spacing: 12,
showsGrid: true,
caption: "Blue squares"
)
let incoming = PanelStyle(
title: " Tile Study \n",
spacing: 30,
showsGrid: false,
caption: nil
)
let expected = PanelStyle(
title: "Tile Study",
spacing: 12,
showsGrid: false,
caption: nil
)
let result = try mergeStyles(base: base, incoming: incoming)
precondition(result == expected)
precondition(base.title == "Shape Study")
precondition(base.caption == "Blue squares")
precondition(incoming.title == " Tile Study \n")
print(result.title)
print("spacing: \(result.spacing)")
print("grid: \(result.showsGrid)")
print("caption cleared: \(result.caption == nil)")
let blankTitle = PanelStyle(
title: " \n\t",
spacing: 30,
showsGrid: false,
caption: nil
)
do {
_ = try mergeStyles(base: base, incoming: blankTitle)
preconditionFailure("Expected emptyTitle")
} catch StyleMergeError.emptyTitle {
print("Rejected blank title")
}
let negativeBase = PanelStyle(
title: "Shape Study",
spacing: -1,
showsGrid: true,
caption: "Blue squares"
)
do {
_ = try mergeStyles(base: negativeBase, incoming: incoming)
preconditionFailure("Expected negativeSpacing")
} catch StyleMergeError.negativeSpacing {
print("Rejected negative spacing")
}
Voer vanuit de map met het bestand het volgende uit:
swift MergeStyles.swift
De verwachte uitvoer staat hieronder. Swift was niet beschikbaar in de onderzoeksomgeving, dus het programma is daar nagekeken maar niet uitgevoerd.
Tile Study
spacing: 12
grid: false
caption cleared: true
Rejected blank title
Rejected negative spacing
De initializer is het centrale hulpmiddel bij de besluitvorming: elk argument verwijst naar het gekozen veld van een invoerwaarde of naar een benoemde tussenwaarde. Geen van beide invoerwaarden wordt gewijzigd. De waardesemantiek van Swift ondersteunt deze aanpak; de velden hier zijn zelf waardetypen. Als je later een veld met een klasse toevoegt, wordt de instantie van die klasse waarnaar verwezen wordt niet onafhankelijk gekopieerd wanneer je de omringende struct kopieert. Structures and Classes.
Validatie vindt plaats vóór het aanmaken van de instantie. Een lege binnenkomende titel veroorzaakt een fout in plaats van stilzwijgend terug te vallen op de basistitel, en negatieve basisspatiëring veroorzaakt een fout in plaats van stilzwijgend op nul te worden begrensd. Als beide voorwaarden niet worden gehaald, wordt emptyTitle als eerste gemeld omdat die guard als eerste wordt uitgevoerd. Aanroepende code moet de fout afhandelen voordat ze een resultaat gebruikt.
Bepaal of een optionele waarde wissen of behouden betekent
In het hoofdvoorbeeld is incoming.caption het volledige gewenste bijschrift. De waarde nil betekent daarom: “het resultaat heeft geen bijschrift”. Zo maakt het contract wissen expliciet, ook al is de representatie gewoon een optionele waarde.
Een andere eis kan bepalen dat een binnenkomende nil betekent: “behoud het basisbijschrift”. Bij dat beleid wordt het argument voor vervanging in de initializer caption: incoming.caption ?? base.caption. De nil-coalescing-operator van Swift kiest de linker operand als die een waarde bevat en gebruikt anders de rechter operand. Dit is een gelabeld vervangingsfragment, geen tweede volledig programma. Nil-coalescing operator.
Met dat terugvalbeleid kun je niet ook dezelfde binnenkomende nil gebruiken om te verzoeken het bijschrift te wissen. Kies eerst de betekenis voordat je deze expressie gebruikt. Een lege tekenreeks is een andere waarde: bij beide beleidsregels blijft een binnenkomend bijschrift van \"\" een leeg bijschrift, tenzij je expliciet normalisatie toevoegt.
Als een toekomstige bewerkingshandeling alle drie de acties nodig heeft — behouden, vervangen en wissen — geef de actie dan afzonderlijk weer. Het volgende zelfstandige, volledige programma illustreert een bewerking die alleen het bijschrift betreft, met een aangepaste enum:
enum CaptionChange {
case keep
case set(String?)
}
func applying(
_ change: CaptionChange,
to caption: String?
) -> String? {
switch change {
case .keep:
return caption
case .set(let replacement):
return replacement
}
}
let original: String? = "Blue squares"
precondition(applying(.keep, to: original) == original)
precondition(applying(.set("Green circles"), to: original)
== "Green circles")
precondition(applying(.set(nil), to: original) == nil)
print("Caption actions checked")
Deze enum is een illustratieve uitbreiding van het contract. Behoud de oorspronkelijke functie met twee waarden wanneer beide invoerwaarden volledige stijlen zijn; introduceer een actietype wanneer de invoer daadwerkelijk bewerkingen beschrijft.
Controleer de prioriteit met bewust verschillende waarden
Een nuttige controle van de samenvoeging laat de concurrerende veldwaarden van elkaar verschillen. Als beide invoerwaarden spatiëring 12 hebben en het raster allebei ingeschakeld is, kan een onjuiste bronkeuze onopgemerkt blijven. In het voorbeeld verschillen daarom de waarden 12 en 30, en ook true en false, zodat het verwachte resultaat die keuzes zichtbaar maakt.
Controleer deze randgevallen aan de hand van het beoogde contract voordat je de functie aanpast:
- De binnenkomende waarde
falsemoetfalseblijven; gebruik geen Booleaanse OR om vervanging uit te drukken. - Het binnenkomende bijschrift
nilmoet volgens het hoofdbeleid het bijschrift wissen. - Omringende witruimte in de titel moet worden verwijderd, terwijl interne spaties behouden blijven.
- Een binnenkomende titel die uitsluitend uit witruimte bestaat, moet een fout veroorzaken, zonder terug te vallen op de basistitel.
- De basisspatiëring
0moet geldig zijn; negatieve basisspatiëring moet een fout veroorzaken. - Als je de argumenten omwisselt, moeten de regels gelden voor de nieuw toegewezen rollen.
Het laatste punt is belangrijk omdat deze samenvoeging afhankelijk is van de volgorde. Als je de invoerwaarden uit het voorbeeld omdraait, is het resultaat \"Shape Study\", spatiëring 30, raster true en bijschrift \"Blue squares\". Dat volgt uit de opgegeven regels. Geef beide argumenten bij aanroepen een naam, zodat hun rollen zichtbaar blijven.
Breid bij het toevoegen van een veld de tabel en de initializer uit en voeg een controle met concurrerende waarden toe. Als een toekomstige geneste stijl meerdere onafhankelijke instellingen bevat, bepaal dan of de volledige geneste waarde wordt vervangen of dat elk veld een eigen prioriteit heeft. De huidige implementatie definieert alleen de vier getoonde velden.
Houd concurrency los van het samenvoegbeleid
Deze berekening is synchroon: ze leest de argumenten, valideert de geselecteerde waarden en retourneert een resultaat of werpt een fout. Er is geen taak of actor nodig om de prioriteit vast te stellen. Swift beschrijft asynchrone functies als functies die kunnen worden onderbroken; asynchrone uitvoering toevoegen bepaalt niet welk veld voorrang heeft. Concurrency.
Het voorbeeld declareert PanelStyle als Sendable om aan te geven dat de waarden concurrencygrenzen kunnen overschrijden. Apple vereist dat een sendable-struct sendable-leden bevat. Die conformiteit betreft het overdragen van waarden; de veldkeuzeregels komen volledig van mergeStyles. Sendable.
Houd voor deze lokale taak met het tekenpaneel het contract bij de functie en gebruik invoerwaarden die zichtbaar van elkaar verschillen. Het resultaat moet per veld uit te leggen zijn: een genormaliseerde binnenkomende titel, behouden basisspatiëring, de binnenkomende rasterinstelling en een binnenkomend bijschrift waarmee de vorige waarde expliciet kan worden gewist.
Bronnen
- Structures and Classes — Swift.org.
- merge(_:uniquingKeysWith:) — Apple Developer Documentation.
- trimmingCharacters(in:) — Apple Developer Documentation.
- Install Swift — macOS — Swift.org.
- Getting Started — Swift.org.
- ??(::) — Apple Developer Documentation.
- Concurrency — Swift.org.
- Sendable — Apple Developer Documentation.