Ich kaufte "Einführung in Designmuster, die in Java gelernt wurden [^ 1]" von Professor Hiroshi Yuki, der für "Math Girl" [^ 2] berühmt ist. Da dies eine großartige Gelegenheit ist, werde ich mir Notizen machen, während ich sie mit meinem üblichen Swift implementiere. Dieser Artikel wurde unter Bezugnahme auf "Kapitel 1 Iterator Zählen nacheinander" erstellt.
Als ich das Iterator-Muster zum ersten Mal berührte (es war ein JavaScript-Iterator), hatte ich keine Ahnung, worüber ich mich freute. Dieses Mal habe ich das Gefühl, dass ich die Freude am Iterator-Muster irgendwie erkannt habe, als ich meine Hände bewegte, um es konkret umzusetzen, und den Quellcode von Swifts "Sequenz" und "Karte" betrachtete. Ich hoffe, es wird für diejenigen hilfreich sein, die ähnliche Gefühle haben!
Ein Entwurfsmuster, das beim Scannen einer ganzen Sammlung von Dingen nacheinander verwendet wird, z. B. Array und Wörterbuch. Es ist ein etwas langes Zitat, aber in "Einführung in in Java-Sprache erlernte Entwurfsmuster [^ 1]" wird es wie folgt erklärt.
Wenn i in i ++ der for-Anweisung um 1 erhöht wird, bedeutet dies, dass die gesamten Elemente des Arrays arr von Anfang an in der richtigen Reihenfolge gescannt werden. Im Entwurfsmuster wird die Funktion der hier verwendeten Variablen i abstrahiert und verallgemeinert. Iteratormuster Wir nennen es>. Das Iterator-Muster dient zur Verarbeitung, wenn viele Dinge gesammelt werden, zeigt auf sie in der richtigen Reihenfolge und scannt das Ganze. Das englische Wort iterate bedeutet, etwas zu "wiederholen". [^ 3]
Die Schnittstelle des Typs, der das Element scannt. Es scheint, dass es verschiedene Schulen gibt, aber das folgende Protokoll kann als einfach angesehen werden.
protocol MyIteratorProtocol {
//Art des Elements, das vom Array usw. gehalten wird.
associatedtype Element
//Eine Methode, die zurückgibt, wenn das nächste Element vorhanden ist
func hasNext() -> Bool
//Eine Methode, die während eines Scans das nächste Element zurückgibt
mutating func next() -> Element?
}
Es ist eine Schnittstelle eines Typs, die eine Sammlung von "etwas" wie ein Array oder ein Wörterbuch enthält und über eine Methode zum Generieren eines Iterators verfügt.
protocol AggregateProtocol {
//Art des Elements, das vom Array usw. gehalten wird.
associatedtype Element
//Der von der Iteratormethode zurückgegebene Iteratortyp Element muss übereinstimmen
associatedtype Iterator: MyIteratorProtocol where Iterator.Element == Element
//Methode zum Generieren eines Iterators
func iterator() -> Iterator
}
ConcreteIterator / ConcreteAggregate
Dies sind konkrete Typen, die die Iterator-Schnittstelle und die Aggregat-Schnittstelle implementieren (konform).
Hier werden wir als Beispiel den BirdCage-Typ implementieren, der eine Reihe von Bird-Typen enthält, und seinen Iterator, den BirdCageIterator.
Bird
Eine Struktur, die nur den Namen enthält.
Bird.swift
struct Bird {
let name: String
}
BirdCage
Definiert eine Struktur, die eine Reihe von Vögeln enthält. Implementieren Sie die Iteratormethode, indem Sie den zugehörigen Typalias so einstellen, dass er dem Aggregatprotokoll entspricht. Der Einfachheit halber behalten wir das Bird-Set selbst als Array bei.
BirdCage.swift
struct BirdCage: AggregateProtocol {
//Legen Sie einen bestimmten Typ für AssociatedType fest
typealias Element = Bird
typealias Iterator = BirdCageIterator
//Implementieren Sie eine Methode, die einen Iterator zurückgibt
func iterator() -> Iterator {
BirdCageIterator(birds: birds)
}
//Hier behalten wir die Menge der Einfachheit halber als Array bei
private var birds: [Bird] = []
//Methode, um Vogel in Vogelkäfig zu setzen
mutating func put(bird: Bird) {
birds.append(bird)
}
//Geben Sie den Index an und sehen Sie sich den Vogel an
func peek(birdOf index: Int) -> Bird? {
index < birds.count ? birds[index] : nil
}
}
BirdCateIterator
Definiert einen Iterator für BirdCate. Implementieren Sie die Methode hasNext / next, indem Sie den Alias "AssociatedType" so einstellen, dass er dem MyIteratorProtocol entspricht.
BirdCateIterator.swift
struct BirdCageIterator: MyIteratorProtocol {
//
typealias Element = Bird
//
private let birds: [Bird]
private var index: Int = 0
init(birds: [Bird]) {
self.birds = birds
}
func hasNext() -> Bool {
index < birds.count
}
//
mutating func next() -> Bird? {
let bird = index < birds.count ? birds[index] : nil
index += 1
return bird
}
}
Wenn die Methode aufgerufen wird, die die Menge zum Scannen in einem Array enthält, erweitern Sie ihren eigenen Index um eins. Definieren und rufen Sie eine Funktion auf, die jedes Element der zu verwendenden Menge druckt. Der Punkt besteht darin, die Funktion mit Typparametern wie "Aggregate: AggregateProtocol" zu definieren. Auf diese Weise implementierte Funktionen können für alle im Iterator-Muster implementierten Typen verwendet werden (Typen, die dem "AggregateProtocol" entsprechen).
func printElements<Aggregate: AggregateProtocol>(of aggregate: Aggregate) {
var iterator = aggregate.iterator()
while iterator.hasNext() {
let element = iterator.next()
print(String(describing: element!))
}
}
var cage = BirdCage()
cage.put(bird: Bird(name: "Budgerigar"))
cage.put(bird: Bird(name: "Cockatiel"))
cage.put(bird: Bird(name: "Parakeet"))
cage.put(bird: Bird(name: "Parrot"))
printElements(of: cage)
// Bird(name: "Budgerigar")
// Bird(name: "Cockatiel")
// Bird(name: "Parakeet")
// Bird(name: "Parrot")
Die zuvor erwähnte Funktion "printElements" ist nicht für den konkreten Typ "BirdCage" implementiert, sondern für "Aggregate" general "basierend auf" Aggregate Protocol ". Unabhängig davon, wie der spezifische "BirdCage" oder "BirdCageIterator" die "Iterator" - oder "next" -Methode implementiert, kann "printElements" das Argument "aggregate: Aggregate" verwenden. Ich kann es schaffen
Um diesen Vorteil zu nutzen und "printElements" bequemer zu verwenden, definieren Sie "printElements" in der Erweiterung von "AggregateProtocol".
AggregateProtocol.swift
extension AggregateProtocol {
func printEelements() {
var iterator = self.iterator()
while iterator.hasNext() {
let element = iterator.next()
print(String(describing: element!))
}
}
}
var cage = BirdCage()
cage.put(bird: Bird(name: "Budgerigar"))
cage.put(bird: Bird(name: "Cockatiel"))
//Sie können printElements von jeder Struktur aus aufrufen, die dem Aggregate Protocol entspricht!
cage.printElements()
Tatsächlich hat Swift ein Standard-Iterator-Protokoll [^ 4].
public protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
Und das "Sequence" -Protokoll, dem verschiedene Strukturen wie Array, Dictionary und String entsprechen, verwendet intern das "Iterator Protocol" [^ 5].
public protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
__consuming func makeIterator() -> Iterator
...
}
Die Definition dieses Teils ist das in diesem Artikel selbst erstellte "Aggregate Protocol". Genau dieses "Sequenz" -Protokoll wird unter Verwendung des Iterator-Musters implementiert.
Beispielsweise ist die in Array bekannte "map" -Methode tatsächlich in der Erweiterung des "Sequence" -Protokolls implementiert. Daher können Sie die "map" -Methode nicht nur für Array und Range, sondern auch für Dictionary und String aufrufen.
[1, 2, 3].map { $0 + 1 }
(0...2).map { $0 * 2 }
["key1": 1, "key2": 2].map { (k, i) in i }
"Hello, world!".map { $0.uppercased() }
Und innerhalb der map
-Methode wird der Iterator verwendet [^ 6].
Die interne Implementierung von Array, Dictionary, Range und String sowie die jeweils definierte Implementierung von Iterator sollten unterschiedlich sein, aber die "map" -Methode kann in einer Implementierung über die Schnittstelle von "IteratorProtocol" implementiert werden.
extension Sequence {
...
@inlinable
public func map<T>(
_ transform: (Element) throws -> T
) rethrows -> [T] {
let initialCapacity = underestimatedCount
var result = ContiguousArray<T>()
result.reserveCapacity(initialCapacity)
var iterator = self.makeIterator()
// Add elements up to the initial capacity without checking for regrowth.
for _ in 0..<initialCapacity {
result.append(try transform(iterator.next()!))
}
// Add remaining elements, if any.
while let element = iterator.next() {
result.append(try transform(element))
}
return Array(result)
}
}
Haben Sie die Freude am Iteratormuster gespürt? Bleib dran für das nächste Muster!
[^ 1]: Einführung in Entwurfsmuster, die in der erweiterten und überarbeiteten Java-Sprache gelernt wurden [^ 2]: Ab dem 1. September 2020 ist die Kindle-Version während des Sommerschlussverkaufs zum halben Preis erhältlich! [^ 3]: Ergänzende überarbeitete Ausgabe Einführung in in Java gelernte Entwurfsmuster Kapitel 1 Iterator Zählen nacheinander
Recommended Posts