Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper implementation of Sequence Protocol in a Class

I have been working on learning something about the Accelerate framework and am writing a Vector class to go along with my learning experience. I decided I needed to implement the Sequence protocol and after several false starts and much searching for relevant information to my problem finally came up with a solution that seems to work. Not sure if my solution is proper or not and would like comment if there are better ways to do this. Current code is a bit long but not super long so I will post it here.

import Foundation
import Accelerate

public class Vdsp{
public class VectorD: Sequence, IteratorProtocol {
    var vindex = 0

    public func makeIterator() -> Double? {
        return next()
    }
    public func next() -> Double? {
        let nextIndex = self.vindex * self.s + self.o
        guard self.vindex < self.l  && nextIndex < self.count
            else {
                self.vindex = 0
                return nil
        }
        self.vindex += 1
        return self.data[nextIndex]
    }

    public let count : Int
    fileprivate var l: Int
    fileprivate var o: Int
    fileprivate var s: Int
    public var length : Int {
        get {
            return self.l
        }
        set (value){
            let l = (value - 1) * self.s + self.o
            if l < 0 || l >= self.count {
                preconditionFailure("length exceeds vector boundary")
            }
            self.l = value
        }
    }
    public var stride : Int {
        get {
            return self.s
        }
        set (value){
            let l = (self.l - 1) * value + self.o
            if l < 0 || l >= self.count {
                preconditionFailure("stride will cause vector to exceed vector boundary")
            }
            self.s = value
        }
    }
    public var offset : Int {
        get {
            return self.o
        }
        set (value){
            let l = (self.l - 1) * self.s + value
            if l < 0 || l >= self.count {
                preconditionFailure("stride will cause vector to exceed vector boundary")
            }
            self.o = value
        }
    }
    // length * stride + offset >= 0 and <= count
    public var data : Array<Double>
    public init(length: Int){
        self.count = length
        self.l = length
        self.s = 1
        self.o = 0
        data = Array(repeating: 0.0, count: count)
    }
    // MARK: - Utility functions
    public var empty : VectorD { // Create a new vector unit stride, zero offset
        get{
            return VectorD(length: length)
        }
    }
    public func display(decimals: Int) -> String {
        let fmt = String("%0." + String(decimals) + "f\n")
        var aString = ""
        for i in 0..<length {
            aString += String(format: fmt!, self.data[offset + i * stride])
        }
        return aString
    }
    // MARK: - Subscripts and Operators
    public subscript(index: Int) -> Double {
        get {
            if index > length {
                preconditionFailure("index \(index) out of bounds")
            } else {
                return data[self.offset + index * self.stride]
            }
        }
        set(newValue) {
            if index > self.length {
                preconditionFailure("index \(index) out of bounds")
            } else {
                self.data[self.offset + index * self.stride] = newValue
            }
        }
    }


    public static func + (left: VectorD, right: VectorD) -> VectorD {
        return Vdsp.add(left, right)
    }
    public static func + (left: Double, right: VectorD) -> VectorD {
        return Vdsp.add(left, right)
    }
    public static func + (left: VectorD, right: Double) -> VectorD {
        return Vdsp.add(right, left)
    }
    public static func * (left: VectorD, right: VectorD) -> VectorD {
        return Vdsp.mul(left, right)
    }
    public static func * (left: Double, right: VectorD) -> VectorD {
        return Vdsp.mul(left, right)
    }
    public static func * (left: VectorD, right: Double) -> VectorD {
        return Vdsp.mul(right, left)
    }

    // MARK: - vDSP routines as methods of VectorD
    public func fill(value: Double){
        var v = value
        vDSP_vfillD(&v, &data + offset, stride, vDSP_Length(length))
    }
    public func ramp(start: Double, increment: Double){
        var s = start
        var i = increment
        vDSP_vrampD(&s, &i, &data + offset, stride, vDSP_Length(length))
    }
    public var sumval : Double {
        get {
            var s : Double = 0.0
            vDSP_sveD(&data + offset, stride, &s, vDSP_Length(length))
            return s
        }
    }

}
// MARK: - vDSP routines as class functions of Vdsp
public static func add(_ v1: VectorD, _ v2: VectorD) -> VectorD {
    let v3 = v1.empty
    vDSP_vaddD( &v1.data + v1.offset, v1.stride, &v2.data + v2.offset , v2.stride, &v3.data, 1, vDSP_Length(v3.length))
    return v3
}
public static func add(_ s: Double, _ v: VectorD) -> VectorD {
    var sdta = s
    let r = v.empty
    vDSP_vsaddD( &v.data + v.offset, v.stride, &sdta, &r.data, 1, vDSP_Length(v.length))
    return r
}

public static func mul(_ v1: VectorD, _ v2: VectorD) -> VectorD {
    let v3 = v1.empty
    vDSP_vmulD( &v1.data + v1.offset, v1.stride, &v2.data + v2.offset, v2.stride, &v3.data, 1, vDSP_Length(v3.length))
    return v3
}
public static func mul(_ s: Double, _ v: VectorD) -> VectorD {
    var sdta = s
    let r = v.empty
    vDSP_vsmulD( &v.data + v.offset, v.stride, &sdta, &r.data, 1, vDSP_Length(v.length))
    return r
   }
}

I am exercising this with

//: Playground for Accelerate

import UIKit

let V = Vdsp.VectorD(length: 10);V.ramp(start: 0.1, increment: 0.2)
print("Vector V after ramp(0.1,0.2)");print(V.display(decimals: 3))
V.length = 4
V.stride = 2
V.offset = 1
print("Vector V after attribute modification")
print(V.display(decimals: 3))
let Q = V.empty
Q.ramp(start: 1.0, increment: 1.0)
print("Vector Q after ramp(1.0,1.0)");print(Q.display(decimals: 3))
print("V * Q"); var R = V * Q
for i in 0..<V.length {
   print("\(V[i]) * \(Q[i]) =  \(R[i])")
}
R = V + Q; print("V + Q = R")
for i in 0..<V.length {
    print("\(V[i]) + \(Q[i]) =  \(R[i])")
}
print("\n")
for item in V.data {
   print(item)
}
print("\n")
for item in V {
   print(item)
}

print("\n")
V.offset = 3
for item in V {
   print(item)
}

and I seem to get the proper output. The Sequence protocol is in the first few lines of the VectorD class.

like image 281
JVSIP Avatar asked Mar 08 '26 23:03

JVSIP


1 Answers

Your implementation of the Sequence protocol is not correct.

First, your makeIterator() method is not used at all because it has the wrong signature, it does not return an Iterator. (You can remove that function from your code without changing anything.) Iterating over the vector elements works because there is a default implementation of makeIterator() for all iterators which are declared to conform to Sequence.

Second, your next() method uses an instance variable vindex which is reset to zero after reaching the end of the iteration. In other words, it is assumed that all elements are retrieved before iterating the same vector again. This gives the unexpected output:

let V = Vdsp.VectorD(length: 10)
V.ramp(start: 1.0, increment: 1.0)

print(Array(V.prefix(4))) // [1.0, 2.0, 3.0, 4.0]
print(Array(V.prefix(4))) // [5.0, 6.0, 7.0, 8.0]
print(Array(V.prefix(4))) // [9.0, 10.0]
print(Array(V.prefix(4))) // [1.0, 2.0, 3.0, 4.0]

Here is a possible implementation of the Sequence protocol:

public class VectorD: Sequence {

    public func makeIterator() -> AnyIterator<Double> {
        var vindex = 0
        return AnyIterator {
            let nextIndex = vindex * self.s + self.o
            guard vindex < self.l  && nextIndex < self.count else {
                return nil
            }
            vindex += 1
            return self.data[nextIndex]
        }
    }

    // ...

}

Note that vindex is now a local variable of makeIterator() and captured by the closure. Calling makeIterator() again will start from the beginning even if the previous iteration did not retrieve all elements:

print(Array(V.prefix(4))) // [1.0, 2.0, 3.0, 4.0]
print(Array(V.prefix(4))) // [1.0, 2.0, 3.0, 4.0]

Another possible implementation would be

public class VectorD: Sequence {

    public func makeIterator() -> AnyIterator<Double> {
        let upperBound = Swift.min(count, o + l * s)
        let it = Swift.stride(from: o, to: upperBound, by: s)
            .lazy.map { self.data[$0] }.makeIterator()
        return AnyIterator(it)
    }

    // ...
}

using the stride() method from the Swift standard library.

like image 63
Martin R Avatar answered Mar 11 '26 14:03

Martin R



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!