不可变结构上的 Swift 惰性 var

问题描述 投票:0回答:1

我正在使用存储数据并对其进行一些缓存操作的结构,例如:

struct Number {
  let value: Int
  lazy var square = pow(Double(value), 2)
  lazy var squareRoot = Double(value).squareRoot()
  lazy var factorial = (1...value).reduce(1, *)
}

它工作正常,除非它是可变的:

var number = Number(value: 9)
number.square // 81
number.squareRoot // 3
number.factorial // 362 880

否则,如果它是恒定的,您会收到以下错误:

let number = Number(value: 9)
number.square // Cannot use mutating getter on immutable value: 'number' is a 'let' constant

有几种流行的解决方案:

1。将 'let' 更改为 'var' 以使其可变

它仅适用于局部变量,不适用于 func 参数,因为默认情况下它们是常量,并且它强制在内部创建副本:

func f(number: Number) {
  var n = number
  n.factorial
}

2。将结构转换为类

这对我来说不是一个选择。

3.使用附加属性来存储缓存结果

根据这些解决方案https://stackoverflow.com/a/32292456/979986https://oleb.net/blog/2015/12/lazy-properties-in-structs-swift/我们可以创建一个专用的类实例来缓存我们所有的计算:

例如:

struct Number {
  let value: Int
  
  class Cache {
    var square: Double?
    var squareRoot: Double?
    var factorial: Int?
  }
  private let cache = Cache()
  ...
  var factorial: Int {
    guard let factorial = cache.factorial else {
      let res = (1...value).reduce(1, *)
      cache.factorial = res
      return res
    }
    return factorial
  }
}

let number = Number(value: 9)
number.factorial // 362 880

但是这个解决方案是多余且不方便的,因为我必须克隆所有结构中的所有属性。

问题:对于大多数情况还有其他方便的方法吗?

swift struct lazy-evaluation
1个回答
0
投票

它需要将缓存值存储在结构之外,以便为不可变实例提供通用解决方案。然后,您可以通过键从计算属性访问此存储,该键可以是此调用出现的代码位置:

class Lazy {
  private static var cache = [Int : Any]()
  
  static func `var`<T>(file: String = #file, line: Int = #line, column: Int = #column, f: () -> T) -> T {
    objc_sync_enter(self)
    defer { objc_sync_exit(self) }
    
    let key = "\(file):\(line):\(column)".hashValue
    guard let value = cache[key] as? T else {
      let value = f()
      cache[key] = value
      return value
    }
    return value
  }
}

它不像常量实例上的惰性属性那样工作:

struct Number {
  let value: Int
  var square: Double { Lazy.var { pow(Double(value), 2) } }
  var squareRoot: Double { Lazy.var { Double(value).squareRoot() } }
  var factorial: Int { Lazy.var { (1...value).reduce(1, *) }}
}


let number = Number(value: 9)
number.square // 81
number.squareRoot // 3
number.factorial // 362 880
© www.soinside.com 2019 - 2024. All rights reserved.