Wang Wei's Translation of Swift Advancement: Set Type Protocol

Posted by AE117 on Wed, 05 Jun 2019 23:26:59 +0200

Related articles:
Swift Advancement (Built-in Set Type) - A Record of the Growth of a Non-disciplinary Ban Nung

2017.08.09

sequence

Sequence protocol is the basis of set type structure. A sequence represents a series of values of the same type that you can iterate over.

Meeting the requirements of the Sequence protocol is very simple. All you need to do is provide a makeIterator() method that returns iterators:

protocol Sequence{
    
    associatedtype Iterator: IteratorProtocol
    func makeIterator() -> Iterator
    
}

iterator

protocol IteratorProtocol{
    
    associatedtype Element
    mutating func next() -> Element?
    
}

You often see Iterator.Element, which is actually defined in Iterator Protocol.

Essentially, the for loop is a short form of the following code:

var some = [1, 2, 3, 4]
var iterator = some.makeIterator()
//For loop is short for this code
while let element = iterator.next() {
    print(element)
}

You can also create a wireless, never-ending sequence. The simplest case is an iterator that keeps returning the same value.

struct ConstantIterator: IteratorProtocol{
    
    typealias Element = Int
    mutating func next() -> Int? {
        return 1
    }
}

var iter = ConstantIterator()
//while let x = iter.next() {
//    print(x)
//    // This is a dead cycle.
//}

Next, let's look at a more interesting example: FibsIterator iterator can produce a Fibonacci sequence.

struct FibsIterator:IteratorProtocol{
    
    var state = (0,1)
    //Adding mutating modifiers to modify your state
    mutating func next() -> Int?{
        
        let upNum = state.0
        state = (state.1, state.0 + state.1)
        return upNum
        
    }
}

var fibs = FibsIterator()
while let fib = fibs.next() {
    //Stop judging, or it will cycle to death.
    guard fib < 145 else {
        break
    }
    print(fib)
}

Compliance with Sequence Protocol

struct PrefixIterator: IteratorProtocol{
    
    let string: String
    var offset: String.Index
    
    init(string:String) {
        self.string = string
        offset = string.startIndex
    }
    
    mutating func next() -> String? {
    
        guard offset < string.endIndex else {
            return nil
        }
        offset = string.index(after: offset)
        return string[string.startIndex..<offset]
        
    }
}


//Remember to block the custom protocol at the beginning, otherwise error will be reported: type'PrefixSequence'does not conform to protocol'Sequence'
struct PrefixSequence: Sequence{
    
    let string: String
    
    func makeIterator() -> PrefixIterator{
        return PrefixIterator.init(string: string)
    }
    
}

var prefix = PrefixSequence.init(string: "Hello")
for char in prefix {
    print(char)
}

var pre = PrefixIterator.init(string: "World")
while let p = pre.next() {
    print(p)
}

//Replace all with capitals
let ma = prefix.map({ $0.uppercased() })
ma

Iterator and Value Semantics

let seq = stride(from: 0, to: 10, by: 1)
var i1 = seq.makeIterator()
i1.next()  //0
i1.next()  //1

var i2 = i1
i2.next()  //2
i2.next()  //3
i1.next()  //2
i1.next()  //3
//i1 and i2 are separate and independent.

//An Iterator Example without Value Semantics
var i3 = AnyIterator(i1)
var i4 = i3

i3.next()  //4
i4.next()  //5
i3.next()  //6
i4.next()  //7
//Sharing an iterator

Function-based iterators and sequences
Fibonacci iterator is defined by a function that returns AnyIterator:

func fibsIterator() -> AnyIterator<Int>{
    
    var state = (0, 1)
    return AnyIterator{
        let upNum = state.0
        state = (state.1, state.0 + state.1)
        return upNum
    }
    
}

let fibsSequence = AnySequence.init(fibsIterator())
Array.init(fibsSequence.prefix(10))  //[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Array.init(fibsSequence.prefix(2))  //[55, 89]

The sequence function introduced in Swift 3 has two versions. First, sequence(first:next:) takes the value of the first parameter as the first element of the sequence, and uses the closure passed in by the next parameter to generate the subsequent elements of the sequence. In the following example, we generate a sequence of random numbers in which each element is smaller than the previous one until it reaches zero:

let randomNumbers = sequence(first: 100) { (previous: UInt32) in
    
    let newValue = arc4random_uniform(previous)
    guard newValue > 0 else {
        return nil
    }
    return newValue
}
Array.init(randomNumbers)  //[100, 14, 8, 6, 2]

The other is sequence(state:next:), which is more powerful because it can save arbitrary variable state between two next closures being called. We can use it to construct Fibonacci sequences through a single method call.

let fibs2 = sequence(state: (0, 1)) { (state: inout(Int, Int)) -> Int? in
    
    let upNum = state.0
    state = (state.1, state.0 + state.1)
    return upNum
    
}

Array.init(fibs2.prefix(10))  //[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Array.init(fibs2.prefix(3))  //[0, 1, 1]

Reading makes me doze off. = Search found that the information conversion rate is not good, or indeed the code foundation is too poor!!!

Topics: Swift