包含延迟初始化器的 Swift 结构的初始化问题

Initialization problem with Swift struct that contains lazy initializers

我为在线编程练习网站上的 Swift 编程练习编写了两个版本的代码。练习如下:

Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. The square of the sum of the first ten natural numbers is (1 + 2 + ... + 10)² = 55² = 3025. The sum of the squares of the first ten natural numbers is 1² + 2² + ... + 10² = 385. Hence the difference between the square of the sum of the first ten natural numbers and the sum of the squares of the first ten natural numbers is 3025 - 385 = 2640.

第一个版本的代码是:

struct Squares {
    let squareOfSum : UInt64
    let sumOfSquares : UInt64
    let differenceOfSquares : UInt64

    init(_ number: UInt64) {
        var sum = ((1 + number) * number) / 2
        squareOfSum = sum * sum
        sum = 0
        for i in 1...number {
            sum = sum + (UInt64)(i * i)
        }
        sumOfSquares = sum
        differenceOfSquares = squareOfSum - sumOfSquares
    }
}
let sqrs = Squares(5)
print(sqrs.differenceOfSquares)

它顺利通过了编译和 运行s。

第二个版本的代码是:

struct Squares {
    let num : UInt64
    lazy var squareOfSum: UInt64 = {
        return ((1...num).reduce(0, +)).squared
    }()
    lazy var sumOfSquares: UInt64 = {
        return ((1...num).map{[=12=].squared}).reduce(0, +)
    }()
    lazy var differenceOfSquares: UInt64 = {return squareOfSum - sumOfSquares }()

    init(_ number: UInt64) {
        num = number
    }
}
extension Numeric {
    var squared: Self {
        return self * self
    }
}
let sqrs = Squares(5)
print(sqrs.differenceOfSquares)

编译代码时,编译器会生成以下消息:

/tmp/0C9C2EC3-543D-486F-BCB0-D181EC48DC82.8NCVch/main.swift:25:7: error: cannot use mutating getter on immutable value: 'sqrs' is a 'let' constant
print(sqrs.differenceOfSquares)
      ^~~~
/tmp/0C9C2EC3-543D-486F-BCB0-D181EC48DC82.8NCVch/main.swift:24:1: note: change 'let' to 'var' to make it mutable
let sqrs = Squares(5)
^~~
var

如果我根据编译器的建议将 let 更改为 var for sqrs,代码将 运行 很好。或者,如果我将类型 struct 更改为 class 以使对象可变,代码也将 运行 很好。但是,在我的第一个版本的代码中,struct 对象和 let 可变定界符的组合没有问题。怎么到了第二个版本,这两个就变得不协调了?虽然我不知道为什么,但我怀疑这可能与第二个版本中的惰性初始化程序有关。

谁能帮忙解释一下?非常感谢您。

惰性属性具有可变的 getter,即访问惰性 属性 可能会改变结构。为什么?想想你第一次访问一个懒惰的属性。在第一次访问之前,那个属性没有值,在你访问它之后,=右边的表达式被计算并赋值给属性。现在 属性 有了一个值。这就是访问惰性 属性 可以更改结构的方式。因此,您需要 var 才能访问惰性属性。

将结构更改为 class 也有帮助,因为现在 sqrs 存储引用,而 let sqrs 仅使 reference对象常量,而不是使对象本身常量。

第一个版本初始化初始化器中的所有属性。您可以在初始化程序中为每个 let 属性 分配一次,这是第一个版本所做的。由于第一个版本中的属性不是惰性的,您可以使用 let 常量访问它们。