我正在制作一个具有非常昂贵的计算导出值的变异结构.所以我想做的是懒惰地计算这个派生值并存储结果,直到结构再次发生变异,此时派生值不再有效,需要重新计算.
(失败)选项1:生成的属性
如果派生值是生成的属性(如下所示),则始终返回正确的值,但始终重新计算.
(失败)选项2:延迟加载的属性
如果它是一个懒惰的属性,那么计算只进行一次……永远.因此,一旦结构发生变异,派生值就会出错,并且不会重新计算.此外,如果我从结构中分配一个常量值,我无法访问该属性.
在Swift 1.2中是否有任何可能的解决方案或者我需要提交雷达吗?
struct Struct {
var value: Int
// Option 1: Generated property
var derivedValue: Int {
println("Doing expensive calculation")
return self.value * 2
}
// Option 2: Lazy property
lazy var derivedValue: Int = {
println("Doing expensive calculation")
return self.value * 2
}()
init(value: Int) {
self.value = value
}
mutating func mutate() {
value = random()
}
}
var test = Struct(value: 2)
test.derivedValue
test.derivedValue // If not lazy,expensive calculation is done again here
test.mutate()
test.derivedValue // If lazy,this has wrong value
let test2 = test
test2.derivedValue // Compiler error if using lazy implementation
使用嵌入式类可以解决变异结构的限制.这允许您使用按值类型,该类型在需要之前不会运行昂贵的计算,但仍会记住之后的结果.
下面的示例数字结构以一种与您描述的方式相似的方式计算并记住其方形属性.数学本身效率低得离谱,但这是解释解决方案的简单方法.
struct Number {
// Store a cache in a nested class.
// The struct only contains a reference to the class,not the class itself,// so the struct cannot prevent the class from mutating.
private class Cache {
var square: Int?
var multiples: [Int: Int] = [:]
}
private var cache = Cache()
// Empty the cache whenever the struct mutates.
var value: Int {
willSet {
cache = Cache()
}
}
// Prevent Swift from generating an unwanted default initializer.
// (i.e. init(cache: Number.Cache,value: Int))
init(value: Int) {
self.value = value
}
var square: Int {
// If the computed variable has been cached...
if let result = cache.square {
// ...return it.
print("I’m glad I don’t have to do that again.")
return result
} else {
// Otherwise perform the expensive calculation...
print("This is taking forever!")
var result = 0
for var i = 1; i <= value; ++i {
result += value
}
// ...store the result to the cache...
cache.square = result
// ...and return it.
return result
}
}
// A more complex example that caches the varying results
// of performing an expensive operation on an input parameter.
func multiple(coefficient: Int) -> Int {
if let result = cache.multiples[coefficient] {
return result
} else {
var result = 0
for var i = 1; i <= coefficient; ++i {
result += value
}
cache.multiples[coefficient] = result
return result
}
}
}
这就是它的表现:
// The expensive calculation only happens once... var number = Number(value: 1000) let a = number.square // “This is taking forever!” let b = number.square // “I’m glad I don’t have to do that again.” let c = number.square // “I’m glad I don’t have to do that again.” // Unless there has been a mutation since last time. number.value = 10000 let d = number.square // “This is taking forever!” let e = number.square // “I’m glad I don’t have to do that again.” // The cache even persists across copies... var anotherNumber = number let f = anotherNumber.square // “I’m glad I don’t have to do that again.” // ... until they mutate. anotherNumber.value = 100 let g = anotherNumber.square // “This is taking forever!”
作为一个更现实的例子,我在日期结构上使用了这种技术,以确保在日历系统之间进行转换的非平凡计算尽可能少地运行.