【发布时间】:2020-03-27 06:35:44
【问题描述】:
如果我对变量 x, y 的唯一了解是它们的类型为 T,它符合 FloatingPoint 协议,我如何计算 atan2(y, x) 的值并将其作为实例返回类型为T?
实际上,我正在尝试实现Vector2D 协议,但遇到了以下方法/属性的问题:
.init(radius r:Component, angle a:Component)
.angle
以下是我的实现:
/*
Swift Tip: Vector Algebra with Protocols
https://www.objc.io/blog/2018/11/29/vector-calculus-with-protocols/
FloatingPoint
https://developer.apple.com/documentation/swift/floatingpoint
*/
import Foundation // cos, sin
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: FloatingPoint
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral a: Component...) {
assert(a.count >= 2, "Must initialize vector with at least 2 values.")
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r:Component, angle a:Component) {
let r = r as! Double // ⛔️doesn't work‼️
let a = a as! Double
let x = r * cos(a) as! Component
let y = r * sin(a) as! Component
self.init(x: x, y: y)
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { // in radians
atan2(y as! Double, x as! Double) as! Component // ⛔️doesn't work‼️
}
public var degrees: Component { angle / .deg } // in degrees
}
【问题讨论】:
-
查看SE-0246 Generic Math(s) Functions(已被接受,但尚未在 Swift 中实现)和Swift Numerics 项目。
-
@MartinR 你现在有什么解决方法吗?
-
您需要 atan2() 来处理所有可能的 FloatingPoint 类型,还是只需要 Float/Double/CGFloat?
-
@MartinR 我在我的问题中添加了更多细节,你能看看吗?
-
问题是标准库中的 Float 和 Double 可以使用 sin/cos/atan2/...。对于泛型 FloatingPoint 类型,必须重新实现这些函数(这不是微不足道的)。请注意,Swift Numerics 项目定义了一个只有 Float/Double/Float80 符合的 Real 类型。
标签: swift swift-protocols math-functions