在Swift中,假设我有一个名为SCNVector3
的{{1}}变量,并且我希望能够通过以下代码设置p
:
p
无论p = [a, b, c]
是[a, b, c]
还是[Float]
。
我该怎么做?
答案 0 :(得分:1)
您无法使其通用,因为您确实需要Float
值来初始化SCNVector3
,但是可以将多个类型与protocol
结合在一起。
在这里,我使用符合SCNVector3
的数组元素制作了ExpressibleByArrayLiteral
protocol ConvertibleToFloat
。该协议具有将值隐蔽到Float
的能力。我已经为Float
,CGFloat
,Double
和Int
实现了它,并且可以根据需要将其扩展为其他类型:
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)