J'ai acheté "Introduction aux modèles de conception appris en langage Java [^ 1]" par le professeur Hiroshi Yuki, qui est célèbre pour "Math Girl" [^ 2]. Puisque c'est une excellente opportunité, je vais prendre des notes d'étude tout en l'implémentant avec mon Swift habituel. Cet article a été créé en référence au "Chapitre 1 Iterator Counting One by One".
Quand j'ai touché pour la première fois au modèle Iterator (c'était un Iterator JavaScript), je n'avais aucune idée de ce qui me plaisait. Cette fois, je sens que j'ai en quelque sorte réalisé la joie du motif Iterator en déplaçant mes mains pour l'implémenter concrètement et en regardant le code source de la «séquence» et de la «carte» de Swift. J'espère que cela sera utile pour ceux qui ont des sentiments similaires!
Un modèle de conception utilisé lors de l'analyse d'une collection entière d'éléments en séquence, tels que Array et Dictionary. C'est une citation un peu longue, mais dans "Introduction aux modèles de conception appris en langage Java [^ 1]", il est expliqué comme suit.
Si i est incrémenté de 1 dans i ++ de l'instruction for, cela signifie que tous les éléments du tableau arr sont balayés dans l'ordre depuis le début. Dans le modèle de conception, la fonction de la variable i utilisée ici est abstraite et généralisée. Modèle d'itérateur Nous l'appelons>. Le modèle Iterator est destiné au traitement lorsque beaucoup de choses sont rassemblées, en les pointant dans l'ordre et en scannant le tout. Le mot anglais iterate signifie «répéter» quelque chose. [^ 3]
L'interface du type qui analyse l'élément. Il semble qu'il existe différentes écoles, mais les protocoles suivants peuvent être considérés comme simples.
protocol MyIteratorProtocol {
//Type d'élément détenu par le tableau, etc.
associatedtype Element
//Une méthode qui renvoie si l'élément suivant est présent
func hasNext() -> Bool
//Une méthode qui renvoie l'élément suivant lors d'un scan
mutating func next() -> Element?
}
C'est une interface d'un type qui contient une collection de "quelque chose" comme un tableau ou un dictionnaire, et a une méthode pour générer un itérateur.
protocol AggregateProtocol {
//Type d'élément détenu par le tableau, etc.
associatedtype Element
//Le type Iterator renvoyé par la méthode iterator, l'élément doit correspondre
associatedtype Iterator: MyIteratorProtocol where Iterator.Element == Element
//Méthode pour générer un itérateur
func iterator() -> Iterator
}
ConcreteIterator / ConcreteAggregate
Ce sont des types concrets qui implémentent (conformes) l'interface Iterator et l'interface Aggregate.
Ici, à titre d'exemple, nous allons implémenter le type BirdCage qui contient un ensemble de types Bird et son Iterator, le BirdCageIterator.
Bird
Une structure qui contient juste le nom.
Bird.swift
struct Bird {
let name: String
}
BirdCage
Définit une structure qui contient un ensemble d'oiseaux. Implémentez la méthode d'itérateur en définissant l'alias associatedType pour se conformer au protocole d'agrégation. Pour plus de simplicité, nous garderons l'Oiseau défini comme un tableau.
BirdCage.swift
struct BirdCage: AggregateProtocol {
//Définir un type spécifique pour associatedType
typealias Element = Bird
typealias Iterator = BirdCageIterator
//Implémenter une méthode qui retourne un itérateur
func iterator() -> Iterator {
BirdCageIterator(birds: birds)
}
//Ici, nous conservons l'ensemble sous forme de tableau pour plus de simplicité
private var birds: [Bird] = []
//Méthode pour mettre l'oiseau en cage à oiseaux
mutating func put(bird: Bird) {
birds.append(bird)
}
//Spécifiez l'index et regardez l'oiseau
func peek(birdOf index: Int) -> Bird? {
index < birds.count ? birds[index] : nil
}
}
BirdCateIterator
Définit un itérateur pour BirdCate. Implémentez la méthode hasNext / next en définissant l'alias associatedType pour se conformer à MyIteratorProtocol.
BirdCateIterator.swift
struct BirdCageIterator: MyIteratorProtocol {
//Définir un type spécifique pour associatedType
typealias Element = Bird
//Tenez un tableau d'ensembles à analyser
private let birds: [Bird]
private var index: Int = 0
init(birds: [Bird]) {
self.birds = birds
}
func hasNext() -> Bool {
index < birds.count
}
//Lorsque la méthode est appelée, avancez votre index d'une unité
mutating func next() -> Bird? {
let bird = index < birds.count ? birds[index] : nil
index += 1
return bird
}
}
Définissez et appelez une fonction qui imprime chaque élément de l'ensemble.
Le but est de définir la fonction avec des paramètres de type comme ʻAggregate: AggregateProtocol. Les fonctions implémentées de cette manière peuvent être utilisées pour tous les types implémentés dans le modèle Iterator (types conformes à ʻAggregateProtocol
).
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")
La fonction printElements
mentionnée plus haut n'est pas implémentée pour le type BirdCage
concret, mais pour le "ʻAggregate general" qui est conforme au "" Aggregate Protocol
".
Ainsi, quelle que soit la manière dont le spécifique BirdCage
ou BirdCageIterator
implémente la méthode ʻiterator ou
next,
printElements peut utiliser l'argument ʻaggregate: Aggregate
. Je peux le faire.
Pour profiter de cet avantage et utiliser printElements
plus facilement, définissez printElements
dans l'extension de ʻ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"))
//Vous pouvez appeler printElements à partir de n'importe quelle structure conforme au protocole d'agrégation!
cage.printElements()
En fait, Swift est livré en standard avec ʻIterator Protocol` [^ 4].
public protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
Et le protocole Sequence
, auquel se conforment diverses structures comme Array, Dictionary et String, utilise ʻIterator Protocol` en interne [^ 5].
public protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
__consuming func makeIterator() -> Iterator
...
}
La définition de cette partie est le ʻAggregate Protocol` lui-même créé dans cet article. Exactement ce protocole "Séquence" est implémenté en utilisant le modèle Iterator.
Par exemple, la méthode map
, familière avec Array, est en fait implémentée dans l'extension du protocole Sequence
.
Par conséquent, vous pouvez appeler la méthode map
non seulement pour Array et Range, mais aussi pour Dictionary et String.
[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() }
Et à l'intérieur de la méthode map
, l'itérateur est utilisé [^ 6].
L'implémentation interne de Array, Dictionary, Range et String et l'implémentation d'itérateur défini dans chacun devraient être différentes, mais la méthode map
peut être implémentée dans une implémentation via l'interface de ʻIterator Protocol`.
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)
}
}
Avez-vous ressenti la joie du modèle Iterator? Restez à l'écoute pour le prochain modèle!
[^ 1]: Introduction aux modèles de conception appris dans le langage Java augmenté et révisé [^ 2]: Depuis le 1er septembre 2020, la version Kindle est à moitié prix pendant les soldes d'été! [^ 3]: Édition révisée supplémentaire Introduction aux modèles de conception appris en langage Java Chapitre 1 Itérateur comptage un par un