Sequence
A Sequence is a collection of values of the same type and provides the ability to iterate over these values
The most common way to iterate over a Sequence is by for-in, and provides the ability to iterate over these values
for element in someSequence {
doSomething(with: element)
}
We often use for-in loops in data structures such as Array, Dictonary, and set because they implement the Sequence protocol.
Sequence protocol definition:
protocol Sequence{
associatedtype Iterator:IteratorProtocol
func makeIterator()->Iterator
}
The only method of the Sequence protocol that must be implemented is makeIterator(),
MakeIterator () needs to return an Iterator, which is an IteratorProtocol type.
If you provide an Iterator to implement a Sequence, what is an Iterator?
Iterator
Iterator is the IteratorProtocol in the Swift 3.1 standard library. It is used to provide iteration capability for Sequence. For Sequence, we can use for-in to iterate over the elements, but behind the for-in is the IteratorProtocol.
The IteratorProtocol is defined as follows:
public protocol IteratorProtocol{
associatedtype Element
public mutating func next()->Self.Element?
}
Associatedtype declares the type of the element
Next () is used to return the next element in a Sequence, or nil if there is no next element
Examples of Iterator implementation
For example 1
struct SimplestIterator:IteratorProtocol {
typealias Element = Int
mutating func next() -> Int? {
return nil
}
}
The Iterator in this example does not iterate over any elements; rather, the Iterator only calls next() once during iteration.
For example 2
struct ConstantIterator:IteratorProtocol {
typealias Element = Int
mutating func next() -> Int? {
return 1
}
}
This one keeps iterating out 1
Implement a Sequence
Implementing a Sequence starts with implementing an Iterator
Implement an Iterator that takes an array of strings and iterates over the first letter of all the strings in the array. When the last string in the array is iterated over, the iteration is complete and the iteration exits
struct FirstLetterIterator:IteratorProtocol {
typealias Element = String
let stringArr:[String]
var offset:Int
init(strings:[String]) {
stringArr = strings
offset = 0
}
mutating func next() -> String? {
guard offset < stringArr.endIndex else {
return nil
}
let string = stringArr[offset]
offset += 1
return string.substring(to: string.index(string.startIndex, offsetBy: 1))
}
}
Where in next() does the Iterator input an array of strings, judge the bounds, and return the first letter of the string offset, incrementing offset by one
Once you have an implemented Iterator, you can simply implement the Sequence using it and return the Iterator in makeIterator()
struct FirstLetterSequence:Sequence {
let strngs:[String]
func makeIterator() -> FirstLetterIterator {
return FirstLetterIterator(strings:strngs)
}
}
Now that the Sequence implementation is complete,
for letter in FirstLetterSequence(strngs:[“apple”,”banana”,”orange”]) {
print(letter)
}
Print result:
a
b
o
Value type Iterator and reference type Iterator
Value type Iterator
Iterators are generally of value types. A value type Iterator means: When you assign an Iterator to a new variable, you assign the Iterator a copy of all of the Iterator’s states to the new variable. The Iterator continues iterating without affecting the new Iterator.
For example, use the Stride function to create a simple Sequence that starts at 0 and ends at 9, incrementing each time by 1, i.e. [0, 1, 2… 8, 9].
We then get its Iterator and call next() to iterate.
let seq = stride(from: 0, to: 10, by: 1)
var i1 = seq.makeIterator()
print(i1.next())
print(i1.next())
The output
Optional(0)
Optional(1)
And then I do an assignment, and I create a new i2
var i2= i1
Then the output
print(i1.next())
print(i1.next())
print(i2.next())
print(i2.next())
The output
Optional(0)
Optional(1)
Optional(0)
Optional(1)
Here i1 and i2 do not affect each other, assignment makes a complete copy of i1, where the Iterator is an Iterator of value type
An Iterator of the application type
An AnyIterator package can be used to form a reference type Iterator.
var i3 = AnyIterator(i1)
var i4 = i3
The output
print(i3.next())
print(i4.next())
print(i3.next())
print(i4.next())
The output
Optional(0)
Optional(1)
Optional(2)
Optional(3)
An Iterator that refers to a type and is assigned to a new variable affects each other as the new Iterator iterates through the Iterator.
Study blog Reference
Sequence (1) in Swift
Swift Sequence (2)