“ ExpressibleByArrayLiteral”可以通用吗?

时间:2018-07-19 14:33:22

标签: swift

在Swift中,假设我有一个名为SCNVector3的{​​{1}}变量,并且我希望能够通过以下代码设置p

p

无论p = [a, b, c] [a, b, c]还是[Float]

我该怎么做?

2 个答案:

答案 0 :(得分:1)

您无法使其通用,因为您确实需要Float值来初始化SCNVector3,但是可以将多个类型与protocol结合在一起。

在这里,我使用符合SCNVector3的数组元素制作了ExpressibleByArrayLiteral protocol ConvertibleToFloat。该协议具有将值隐蔽到Float的能力。我已经为FloatCGFloatDoubleInt实现了它,并且可以根据需要将其扩展为其他类型:

import UIKit
import SceneKit

public protocol ConvertibleToFloat {
    var floatValue: Float { get }
}

extension Float: ConvertibleToFloat {
    public var floatValue: Float { return self }
}

extension CGFloat: ConvertibleToFloat {
    public var floatValue: Float { return Float(self) }
}

extension Double: ConvertibleToFloat {
    public var floatValue: Float { return Float(self) }
}

extension Int: ConvertibleToFloat {
    public var floatValue: Float { return Float(self) }
}

extension SCNVector3: ExpressibleByArrayLiteral {
    public typealias ArrayLiteralElement = ConvertibleToFloat

    public init(arrayLiteral: ConvertibleToFloat...) {
        let values = arrayLiteral.map { $0.floatValue } + [0, 0, 0]
        self.init(x: values[0], y: values[1], z: values[2])
    }
}

进行测试:

var sv = SCNVector3(0, 0, 0)

// Initialize with array literal of CGFloat
sv = [CGFloat(1.1), CGFloat(2.2), CGFloat(3.3)]

print(sv) // SCNVector3(x: 1.1, y: 2.2, z: 3.3)

// Initialize with array literal of Float
sv = [Float(1.2), Float(2.3), Float(3.4)]

print(sv) // SCNVector3(x: 1.2, y: 2.3, z: 3.4)

// Not enough values, rest default to 0
sv = [CGFloat(3)]

print(sv) // SCNVector3(x: 3.0, y: 0.0, z: 0.0)

// Initialize with empty array
sv = []

print(sv) // SCNVector3(x: 0.0, y: 0.0, z: 0.0)

// Initilize with [Double]
sv = [1.4, 2.6, 3.7]

print(sv) // SCNVector3(x: 1.4, y: 2.6, z: 3.7)

// Initialize with [Int]
sv = [8, 9, 10]

print(sv) // SCNVector3(x: 8.0, y: 9.0, z: 10.0)

// As in the question
let a: CGFloat = 3.1
let b: CGFloat = 4.2
let c: CGFloat = 5.3

sv = [a, b, c]
print(sv) // SCNVector3(x: 3.1, y: 4.2, z: 5.3)

答案 1 :(得分:0)

我从@vacawama的代码开始的小扩展名:

import UIKit
import SceneKit

// MARK: - RealNumber (protocol)

public protocol RealNumber {
    var realValue: Float { get }
}

// MARK: - RealNumber (operations)

// -a (additive inverse)
public prefix func - (a:RealNumber) -> Float {
    return -a.realValue
}
// a + b (addition)
public func + <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
    return a.realValue + b.realValue
}
// a - b (substraction)
public func - <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
    return a.realValue - b.realValue
}
// a * b (multiplication)
public func * <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
    return a.realValue * b.realValue
}
// a / b (division)
public func / <U:RealNumber, V:RealNumber>(a:U, b:V) -> Float {
    return a.realValue / b.realValue
}

// MARK: - RealNumber (conforming types)

extension Float: RealNumber { public var realValue: Float { return self } }
extension CGFloat: RealNumber { public var realValue: Float { return Float(self) } }
extension Double: RealNumber { public var realValue: Float { return Float(self) } }
extension Int: RealNumber { public var realValue: Float { return Float(self) } }

// MARK: - test RealNumber

let c = CGFloat(3) // CGFloat
let f = Float(2)   // Float
let d = 1.5        // Double
let i = 4          // Int

// test operations
i + d  // 5.5
c - f  // 1.0
i * d  // 6.0
i / d  // 2.667

// MARK: - RealVector (protocol)

public protocol RealVector: ExpressibleByArrayLiteral {

    // must have dimension (of the vector space)
    static var dimension: Int { get }
    // can get/set coords of the vector
    var coordinates: [Float] { get set }

    // must have default init
    init()
    // can init with [RealNumber]
    init(arrayLiteral: RealNumber...)

    // must have vector addition, additive inverse, and scalar multiplication
    static func + (u:Self, v:Self) -> Self       // vector addition
    static prefix func - (u:Self) -> Self        // additive inverse
    static func * (a:RealNumber, v:Self) -> Self // scalar multiplication

    // can get/set coordinate by subscript index
    subscript(i:Int) -> Float { get set }
}

// MARK: - RealVector (default behaviors)

extension RealVector {

    // no default get/set coordinates
    // you have to implement them yourself ‼️ 

    // default subscript implementation
    public subscript(i:Int) -> Float {
        get { return coordinates[i] }
        set {
            var coords = self.coordinates
            coords[i] = newValue
            self.coordinates = coords
        }
    }

    // default u + v (vector addition)
    public static func + (u:Self, v:Self) -> Self {
        var coords = (0...(Self.dimension - 1)).map { u[$0] + v[$0] }
        var vec = Self.init()
        vec.coordinates = coords
        return vec
    }

    // default u + a (convenient vector addition)
    public static func + (u:Self, a:RealNumber) -> Self {
        var coords = (0...(Self.dimension - 1)).map { u[$0] + a.realValue }
        var vec = Self.init()
        vec.coordinates = coords
        return vec
    }

    // default -v (additive inverse)
    static public prefix func - (u:Self) -> Self {
        var coords = (0...(Self.dimension - 1)).map { -u[$0] }
        var vec = Self.init()
        vec.coordinates = coords
        return vec
    }

    // default * (scalar multiplication)
    public static func * (a:RealNumber, v:Self) -> Self {
        var coords = (0...(Self.dimension - 1)).map { a.realValue * v[$0] }
        var vec = Self.init()
        vec.coordinates = coords
        return vec
    }

    // default / (scalar division)
    public static func / (v:Self, a:RealNumber) -> Self {
        return (1/a.realValue) * v
    }

    // other default operations ( u-v, v*a, u+=v, u-=v, u*=a, ... ) 
    public static func - (u:Self, v:Self) -> Self { return u + (-v) }
    public static func - (u:Self, a:RealNumber) -> Self { return u + (-a) }
    public static func * (v:Self, a:RealNumber) -> Self { return a * v }

    public static func += (u:inout Self, v:Self) { u = u + v }
    public static func -= (u:inout Self, v:Self) { u = u - v }

    public static func += (u:inout Self, a:RealNumber) { u = u + a }
    public static func -= (u:inout Self, a:RealNumber) { u = u - a }
    public static func *= (u:inout Self, a:RealNumber) { u = u * a }
    public static func /= (u:inout Self, a:RealNumber) { u = u / a }

    // default init with [RealNumber]
    public init(arrayLiteral elements: RealNumber...) {
        var coords = elements.map { $0.realValue } + [Float](repeating: 0, count: Self.dimension)
        self.init()
        self.coordinates = coords
    }

    // default init with another RealVector
    public init<V:RealVector>(_ v:V) {
        self.init()
        self.coordinates = v.coordinates
    }
}


// vector operations of two (different types of) RealVector
// U + V -> U
public func + <U:RealVector, V:RealVector>(u:U, v:V) -> U {
    return u + U.init(v)
}

// U - V -> U
public func - <U:RealVector, V:RealVector>(u:U, v:V) -> U {
    return u - U.init(v)
}

// MARK: - RealVector (conforming types)

// SCNVector3
extension SCNVector3: RealVector {
    // 3-dimensional
    public static var dimension: Int { return 3 }
    // custom get/set coordinates
    public var coordinates: [Float] {
        get { return [x, y, z] } 
        set {
            var coords = newValue + [0,0,0]
            x = coords[0]
            y = coords[1]
            z = coords[2]
        }
    }
}// end: SCNVector3: RealVector

// CGPoint
extension CGPoint: RealVector {
    // 2-dimensional
    public static var dimension: Int { return 2 }
    // custom get/set coordinates
    public var coordinates: [Float] {
        get { return [x, y].map { $0.realValue } }
        set {
            var coords = newValue + [0,0]
            x = CGFloat(coords[0])
            y = CGFloat(coords[1])
        }
    }
}// end: CGPoint

// CGVector
extension CGVector: RealVector {
    // 2-dimensional
    public static var dimension: Int { return 2 }
    // custom get/set coordinates
    public var coordinates: [Float] {
        get { return [dx, dy].map { $0.realValue } }
        set {
            var coords = newValue + [0,0]
            dx = CGFloat(coords[0])
            dy = CGFloat(coords[1])
        }
    }
}// end: CGVector

// CGSize
extension CGSize: RealVector {
    // 2-dimensional
    public static var dimension: Int { return 2 }
    // custiom get/set coordinates
    public var coordinates: [Float] {
        get { return [width, height].map { $0.realValue } }
        set {
            var coords = newValue + [0,0]
            width = CGFloat(coords[0])
            height = CGFloat(coords[1])
        }
    }
}

// SCNVector4
extension SCNVector4: RealVector {
    // 4-dimensional
    public static var dimension: Int { return 4 }
    // custom get/set coordinates
    public var coordinates: [Float] {
        get { return [x, y, z, w] }
        set {
            var coords = newValue + [0,0,0,0]
            x = coords[0]
            y = coords[1]
            z = coords[2]
            w = coords[3]
        }       
    }
}// end: SCNVector4

// CGRect
extension CGRect: RealVector {
    // 4-dimensional
    public static var dimension: Int { return 4}
    // custom get/set coordinates
    public var coordinates: [Float] {
        get {
            return [origin.x, origin.y, width, height].map { $0.realValue }
        }
        set {
            var v = newValue + [0,0,0,0]
            origin = [v[0], v[1]]
            size = [v[2], v[3]]
        }
    }
}// end: CGRect

// MARK: - test RealVector
var p: CGPoint = [5, 6]
p.coordinates
CGPoint.dimension

// test init 
p = CGPoint(SCNVector3(1,2,3))                  // init with SCNVector3
p = [CGFloat(1.1), CGFloat(2.2), CGFloat(3.3)]  // init with [CGFloat]
p = [Float(1.2), Float(2.3), Float(3.4)]        // init with [Float]
p = [1.2, 3.4, 5.6]                             // init with [Double]
p = [1, 2, 3]                                   // init with [Int]
p = [3]                                         // Not enough values (rest default to 0)
p = []                                          // init with empty array

// test vector subscript
p[1]      // get: 0.0
p[1] = 3  // set: 3.0
p         // (0.0, 3.0)

// test vector operations
let u: SCNVector3 = [1,2,3]
let v: SCNVector3 = [4,5,6]
var w = SCNVector3(p)        // init with CGPoint

w = -u

w = u + [1,2,3]

w = u + v
w = u - v
w = 2 * u
w = u * 2
w = u / 2

w += 0.5    // (1.0, 1.5, 2.0)
w -= 2      // (-1.0, -0.5, 0)
w *= 4      // (-4, -2, 0)
w /= 2      // (-2, -1, 0)
w += [7,8,9]// (5, 7, 9)

// vector linear combinations
w = 4*u - 3*v    // (-8, -7, -6)

// vector operations of two different conforming types
w + p            // (-8, -4, -6)
w - p            // (-8, -10, -6)