我正在尝试制作一个具有惰性计算变量magnitude
的Vector结构,但我似乎无法找到一种方法。
这就是我所拥有的:
struct Vector {
var x: Double = 0.0 {
didSet {
magnitudeActual = -1.0
}
}
var y: Double = 0.0 {
didSet {
magnitudeActual = -1.0
}
}
var magnitudeActual: Double = 0.0
var magnitude: Double {
if magnitudeActual < 0.0 {
magnitudeActual = sqrt(x * x + y * y) //cannot assign to "magnitudeActual" in self
}
return magnitudeActual
}
init() {}
init(_ x: Double, _ y: Double) {
self.x = x
self.y = y
}
}
我已经尝试了许多方法来实现这一点,但似乎没有任何效果。此外,willGet
会很好,但不存在。
答案 0 :(得分:4)
从实例方法中修改值类型
结构和枚举是值类型。默认情况下,无法在其实例方法中修改值类型的属性。
但是,如果需要在特定方法中修改结构或枚举的属性,则可以选择改变该方法的行为。然后,该方法可以从方法中改变(即更改)其属性,并且当方法结束时,它所做的任何更改都将写回原始结构。该方法还可以为其隐式self属性分配一个全新的实例,并且该新实例将在方法结束时替换现有实例。
那么,除非执行修改的函数标记为struct
,否则mutating
无法自行修改。这意味着您需要为属性定义正确的get
函数。
var magnitude: Double {
mutating get {
if magnitudeActual < 0.0 {
NSLog("Recalc") // just to make sure it's caching the result properly.
magnitudeActual = sqrt(x * x + y * y)
}
return magnitudeActual
}
}
现在我们可以做到这一点
var v = Vector(3, 4)
v.magnitude // Recalc 5
v.magnitude // 5
v.x = 5
v.y = 12
v.magnitude // Recalc 13
答案 1 :(得分:1)
我玩了你的代码和Alex提出的变异吸气剂,我发现了
init(_ x: Double, _ y: Double) {
self.x = x
self.y = y
}
似乎没有为x和y调用setter,或者至少不调用通知didSet
和willSet
。如果构造为
magnitudeActual
将为0
var v = Vector(3,4)
并且不会重新计算幅度并返回0.
但如果构造像
var v = Vector()
v.x = 3
v.y = 4
调用setter,magnitudeActual将为-1,并且将重新计算幅度。
根据GoZoner的建议,修复方法是将默认值magnitudeActual
设置为-1。
我不知道,如果这是设计或bug的行为。
我使用的代码:
struct Vector {
var x: Double = 0.0 {
didSet {
println("set x")
magnitudeActual = -1.0
}
}
var y: Double = 0.0 {
didSet {
println("set y")
magnitudeActual = -1.0
}
}
var magnitudeActual: Double = -1.0
var magnitude: Double {
mutating get {
if magnitudeActual < 0.0 {
println("Recalc") // just to make sure it's caching the result properly.
magnitudeActual = sqrt(x * x + y * y)
}
return magnitudeActual
}
}
init() {}
init(_ x: Double, _ y: Double) {
self.x = x
self.y = y
}
}
答案 2 :(得分:0)
为什么不呢:
var magnitude: Double {
return sqrt(x * x + y * y)
}
如果你真的只想计算一次,请使用一个带有懒惰成员的立即执行的闭包:
lazy var magnitude: Double = {
return sqrt(x * x + y * y)
}()
因为它是懒惰的,所以在第一次访问它之前不会尝试进行分配。因为赋值是闭包的结果,所以将为赋值执行闭包,让第一次访问时计算该值。
以下是行为的简单示例:
class MyClass {
var x :Double = 10
lazy var magnitude: Double = {
return self.x
}()
}
var obj = MyClass()
obj.x = 12
obj.magnitude // 12 (magnitude was calculated right here after x was changed)
obj.x = 14
obj.magnitude // 12 (magnitude did not change)
obj.magnitude = 8
obj.magnitude // 8 (it changed)
答案 3 :(得分:0)
当然,你节省很少,很少购买优化magnitude
的计算。你可以简单地完成并撒上灰尘:
class Point {
var x : Double;
var y : Double;
var magnitude : Double { return sqrt (x*x + y*y) }
var angle : Double { return atan (x, y) }
}
但是,如果你确实需要优化,
class Point {
var x : Double { didSet { updateMagnitude() }}
var y : Double { didSet { updateMagnitude() }}
var magnitude : Double
func updateMagnitude () { self.magnitude = sqrt (x*x + y*y) }
init () {
// ...
updateMagnitude()
}
}