Swift 类似于 Equatable 的协议
Swift protocol similar to Equatable
我正在尝试为 Lerp-able(线性可插值)的类型创建一个协议。我声明它类似于 Equatable
的定义:
protocol Lerpable {
func lerp(from: Self, to: Self, alpha: Double) -> Self
}
不幸的是,当我尝试为 Double
实施 Lerpable
时:
func lerp(from: Double, to: Double, alpha: Double) -> Double {
return from + alpha * (to - from)
}
extension Double: Lerpable {}
我得到一个错误:Type 'Double' does not conform to protocol 'Lerpable'
。
我以为这会很简单,但也许我只是不明白 Equatable 是如何工作的。还是Swift的特例?有什么想法吗?
更新:正确答案是,这里是代码的最终版本,供其他人参考:
protocol Lerpable {
func lerp(to: Self, alpha: Double) -> Self
}
extension Double: Lerpable {
func lerp(to: Double, alpha: Double) -> Double {
return self + alpha * (to - self)
}
}
func lerp<T: Lerpable>(from: T, to: T, alpha: Double) -> T {
return from.lerp(to, alpha: alpha)
}
我添加了全局 lerp
函数,所以我仍然可以将其称为
lerp(foo, bar, alpha: 0.5)
而不是
foo.lerp(bar, alpha: 0.5)
要解决您的问题,您需要将 lerp
函数放在 extension
中,如下所示:
extension Double: Lerpable {
func lerp(from: Double, to: Double, alpha: Double) -> Double {
return from + alpha * (to - from)
}
}
如果你看看 Equatable
protocol
:
protocol Equatable {
func == (lhs: Self, rhs: Self) -> Bool
}
您在类型 extension
之外声明它的方法(==
特别是)的原因是因为 Equatable
希望您重载运算符和运算符 must 在全局范围内声明。这里有一个例子来说明:
protocol MyProtocol {
func *** (lhs: Self, rhs: Self) -> Self
}
现在让Int
采用协议:
extension Int : MyProtocol {}
infix operator *** {}
func *** (lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
您可以通过为进度值添加类型别名来进一步抽象实现 't'
public protocol Lerpable {
typealias LerpProgressType
func lerpTo(value: Self, t: LerpProgressType) -> Self
}
public func lerp<T:Lerpable>(from: T, _ to: T, _ t: T.LerpProgressType) -> T {
return from.lerpTo(to, t: t)
}
// implementations
extension Double : Lerpable {
public typealias LerpProgressType = Double
public func lerpTo(value: Double, t: Double) -> Double {
return (1.0 - t) * self + t * value
}
}
extension Float : Lerpable {
public typealias LerpProgressType = Float
public func lerpTo(value: Float, t: Float) -> Float {
return (1.0 - t) * self + t * value
}
}
extension CGFloat : Lerpable {
public typealias LerpProgressType = CGFloat
public func lerpTo(value: CGFloat, t: CGFloat) -> CGFloat {
return (1.0 - t) * self + t * value
}
}
您现在还可以扩展各种结构(例如 CGPoint、CGSize、CGRect、CATransform3D 等):
extension CGPoint : Lerpable {
public typealias LerpProgressType = CGFloat
public func lerpTo(value: CGPoint, t: CGFloat) -> CGPoint {
return
CGPoint(
x: x.lerpTo(value.x, t),
y: y.lerpTo(value.y, t)
)
}
}
extension CLLocationCoordinate2D : LinearInterpolation {
public typealias LerpProgressType = CLLocationDegrees
public func lerpTo(value: CLLocationCoordinate2D, t: CLLocationDegrees) -> CLLocationCoordinate2D {
return
CLLocationCoordinate2D(
latitude: latitude.lerpTo(value.latitude, t),
longitude: longitude.lerpTo(value.longitude, t)
)
}
}
要在泛型结构中实现线性插值:
public struct ValueRange<T> {
public var start:T
public var end:T
}
extension ValueRange where T:Lerpable {
public func lerp(t: T.LerpProgressType) -> T {
return start.lerpTo(end, t: t)
}
}
我正在尝试为 Lerp-able(线性可插值)的类型创建一个协议。我声明它类似于 Equatable
的定义:
protocol Lerpable {
func lerp(from: Self, to: Self, alpha: Double) -> Self
}
不幸的是,当我尝试为 Double
实施 Lerpable
时:
func lerp(from: Double, to: Double, alpha: Double) -> Double {
return from + alpha * (to - from)
}
extension Double: Lerpable {}
我得到一个错误:Type 'Double' does not conform to protocol 'Lerpable'
。
我以为这会很简单,但也许我只是不明白 Equatable 是如何工作的。还是Swift的特例?有什么想法吗?
更新:正确答案是
protocol Lerpable {
func lerp(to: Self, alpha: Double) -> Self
}
extension Double: Lerpable {
func lerp(to: Double, alpha: Double) -> Double {
return self + alpha * (to - self)
}
}
func lerp<T: Lerpable>(from: T, to: T, alpha: Double) -> T {
return from.lerp(to, alpha: alpha)
}
我添加了全局 lerp
函数,所以我仍然可以将其称为
lerp(foo, bar, alpha: 0.5)
而不是
foo.lerp(bar, alpha: 0.5)
要解决您的问题,您需要将 lerp
函数放在 extension
中,如下所示:
extension Double: Lerpable {
func lerp(from: Double, to: Double, alpha: Double) -> Double {
return from + alpha * (to - from)
}
}
如果你看看 Equatable
protocol
:
protocol Equatable {
func == (lhs: Self, rhs: Self) -> Bool
}
您在类型 extension
之外声明它的方法(==
特别是)的原因是因为 Equatable
希望您重载运算符和运算符 must 在全局范围内声明。这里有一个例子来说明:
protocol MyProtocol {
func *** (lhs: Self, rhs: Self) -> Self
}
现在让Int
采用协议:
extension Int : MyProtocol {}
infix operator *** {}
func *** (lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
您可以通过为进度值添加类型别名来进一步抽象实现 't'
public protocol Lerpable {
typealias LerpProgressType
func lerpTo(value: Self, t: LerpProgressType) -> Self
}
public func lerp<T:Lerpable>(from: T, _ to: T, _ t: T.LerpProgressType) -> T {
return from.lerpTo(to, t: t)
}
// implementations
extension Double : Lerpable {
public typealias LerpProgressType = Double
public func lerpTo(value: Double, t: Double) -> Double {
return (1.0 - t) * self + t * value
}
}
extension Float : Lerpable {
public typealias LerpProgressType = Float
public func lerpTo(value: Float, t: Float) -> Float {
return (1.0 - t) * self + t * value
}
}
extension CGFloat : Lerpable {
public typealias LerpProgressType = CGFloat
public func lerpTo(value: CGFloat, t: CGFloat) -> CGFloat {
return (1.0 - t) * self + t * value
}
}
您现在还可以扩展各种结构(例如 CGPoint、CGSize、CGRect、CATransform3D 等):
extension CGPoint : Lerpable {
public typealias LerpProgressType = CGFloat
public func lerpTo(value: CGPoint, t: CGFloat) -> CGPoint {
return
CGPoint(
x: x.lerpTo(value.x, t),
y: y.lerpTo(value.y, t)
)
}
}
extension CLLocationCoordinate2D : LinearInterpolation {
public typealias LerpProgressType = CLLocationDegrees
public func lerpTo(value: CLLocationCoordinate2D, t: CLLocationDegrees) -> CLLocationCoordinate2D {
return
CLLocationCoordinate2D(
latitude: latitude.lerpTo(value.latitude, t),
longitude: longitude.lerpTo(value.longitude, t)
)
}
}
要在泛型结构中实现线性插值:
public struct ValueRange<T> {
public var start:T
public var end:T
}
extension ValueRange where T:Lerpable {
public func lerp(t: T.LerpProgressType) -> T {
return start.lerpTo(end, t: t)
}
}