懒惰的计算变量

时间:2014-07-30 22:26:42

标签: swift

我正在尝试制作一个具有惰性计算变量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会很好,但不存在。

4 个答案:

答案 0 :(得分:4)

From the docs:

  

从实例方法中修改值类型

     

结构和枚举是值类型。默认情况下,无法在其实例方法中修改值类型的属性。

     

但是,如果需要在特定方法中修改结构或枚举的属性,则可以选择改变该方法的行为。然后,该方法可以从方法中改变(即更改)其属性,并且当方法结束时,它所做的任何更改都将写回原始结构。该方法还可以为其隐式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,或者至少不调用通知didSetwillSet。如果构造为

,那么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()
  }
}