0

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

求和的平方与前 N 个自然数的平方和之间的差。前十个自然数的平方和为 (1 + 2 + ... + 10)² = 55² = 3025。前十个自然数的平方和为 1² + 2² + ... + 10² = 385。因此,前十个自然数之和的平方和前十个自然数的平方和之差为 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)

它通过编译并运行没有问题。

第二个版本的代码是:

struct Squares {
    let num : UInt64
    lazy var squareOfSum: UInt64 = {
        return ((1...num).reduce(0, +)).squared
    }()
    lazy var sumOfSquares: UInt64 = {
        return ((1...num).map{$0.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

如果我按照编译器的建议更改letvarfor sqrs,代码将运行良好。或者,如果我将类型更改structclass使对象可变,代码也将运行良好。struct但是,在我的第一个版本的代码中,对象和let可变性分隔符的组合没有问题。怎么在第二个版本中,这两个变得不一致?虽然我不知道为什么,但我怀疑这可能与第二个版本中的惰性初始化器有关。

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

4

1 回答 1

1

惰性属性具有变异的 getter,即访问惰性属性可能会改变结构。为什么?想想你第一次访问惰性属性的时候。在第一次访问之前,该属性没有值,在您访问它之后,右边的表达式=被评估并分配给该属性。现在该属性具有价值。这就是访问惰性属性可以更改结构的方式。因此,您需要var访问惰性属性。

将结构更改为类也有帮助,因为现在sqrs存储了一个引用,并且let sqrs只使对对象的引用成为常量,而不是使对象本身成为常量。

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

于 2020-07-17T00:36:33.787 回答