我喝了Swift中的结构/值。现在我有一个有趣的问题,我不知道该如何解决。我有一个结构是容器,例如
def run(question):
score = 0
for index, getInput in enumerate(question):
answer = input(getInput.prompt)
if (index == 0) and (answer == getInput.answer):
score += 30
elif answer == getInput.answer:
score += 35
print("You got " + str(score) + "/" + '100' + " correct!")
在对此进行编辑时,我会创建副本,以便保留撤消堆栈。到目前为止,一切都很好。就像显示的好教程一样。不过,我与此人一起使用了一些派生属性:
struct Foo {
var bars:[Bar]
}
在最近的分析中,我注意到a)计算派生Value的计算是昂贵/冗余的b)在各种用例中并不总是必须进行计算。
按照我经典的OOP方式,我会将其设置为记忆变量/惰性变量。基本上,在调用之前将其设置为零,对其进行一次计算并存储,然后在以后的调用中返回所述结果。由于我遵循“制作副本以进行编辑”模式,因此不变性不会被破坏。
但是,如果它是struct,我不知道如何应用该模式。我可以这样做:
struct Foo {
var bars:[Bar]
var derivedValue:Int {
...
}
}
起作用,直到结构引用该值本身为止,例如
struct Foo {
var bars:[Bar]
lazy var derivedValue:Int = self.computeDerivation()
}
这时,编译器抱怨是因为struct Foo {
var bars:[Bar]
lazy var derivedValue:Int = self.computeDerivation()
fun anotherDerivedComputation() {
return self.derivedValue / 2
}
}
导致接收方发生更改,因此需要标记为anotherDerivedComputation
。使访问器被标记为变异只是感觉不对。但是对于咧嘴笑,我尝试了一下,但是这带来了一系列新的问题。现在在任何我有类似表情的地方
mutating
编译器抱怨,因为参数隐式是一个不变的let值,而不是var。
是否存在一种结构具有延迟/惰性/缓存成员的结构?
答案 0 :(得分:3)
记忆不会在结构内部发生。记忆的方法是将字典 off存储在单独的空间中。关键在于得出值的方式,而值就是一次计算的值。您可以将其设置为struct类型的静态对象,就像命名空间一样。
struct S {
static var memo = [Int:Int]()
var i : Int
var square : Int {
if let result = S.memo[i] {return result}
print("calculating")
let newresult = i*i // pretend that's expensive
S.memo[i] = newresult
return newresult
}
}
var s = S(i:2)
s.square // calculating
s = S(i:2)
s.square // [nothing]
s = S(i:3)
s.square // calculating
答案 1 :(得分:2)
我知道完成这项工作的唯一方法是将惰性成员包装在一个类中。这样,包含对对象的引用的结构可以保持不变,而对象本身可以被突变。
几年前,我写了一篇有关该主题的博客文章:Lazy Properties in Structs。它会详细介绍细节,并为包装类的设计提供两种不同的方法,具体取决于惰性成员是否需要结构中的实例信息来计算缓存的值。
答案 2 :(得分:1)
我将问题概括为一个更简单的问题:一个x,y Point结构,它希望懒惰地计算/缓存r(adius)的值。我将ref包装器放在块封闭周围,并提出了以下内容。我称它为“一次”块。
import Foundation
class Once<Input,Output> {
let block:(Input)->Output
private var cache:Output? = nil
init(_ block:@escaping (Input)->Output) {
self.block = block
}
func once(_ input:Input) -> Output {
if self.cache == nil {
self.cache = self.block(input)
}
return self.cache!
}
}
struct Point {
let x:Float
let y:Float
private let rOnce:Once<Point,Float> = Once {myself in myself.computeRadius()}
init(x:Float, y:Float) {
self.x = x
self.y = y
}
var r:Float {
return self.rOnce.once(self)
}
func computeRadius() -> Float {
return sqrtf((self.x * self.x) + (self.y * self.y))
}
}
let p = Point(x: 30, y: 40)
print("p.r \(p.r)")
我选择让OnceBlock接受输入,因为否则将其初始化为具有引用self的函数会很痛苦,因为在初始化时还不存在self,因此更容易推迟该链接到缓存/调用站点(var r:Float
)