包含惰性初始值设定项的Swift结构的初始化问题

包含惰性初始值设定项的Swift结构的初始化问题,swift,class,struct,lazy-initialization,mutating-function,Swift,Class,Struct,Lazy Initialization,Mutating Function,我在一个在线编程练习网站上为Swift编程练习编写了两个版本的代码。行动如下: 求和的平方与前N个自然数的平方和之间的差。 前十个自然数之和的平方为(1+2+…+10)²=55²=3025。 前十个自然数的平方和为1²+2²+…+10² = 385. 因此,前十个自然数的平方和与前十个自然数的平方和之间的差值为3025-385=2640 第一个版本的代码是: struct Squares { let squareOfSum : UInt64 let sumOfSquares :

我在一个在线编程练习网站上为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)
它通过了编译,运行起来没有问题

第二个版本的代码是:

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
如果按照编译器的建议,将
sqrs
let
更改为
var
,代码将运行良好。或者,如果我将类型
struct
更改为
class
,以使对象可变,那么代码也会运行良好。但是,在我的第一个版本中的代码中,
struct
对象和
let
可变定界符的组合没有问题。为什么在第二个版本中,这两个变得不一致?虽然我不知道为什么,但我怀疑这可能与第二个版本中懒惰的初始值设定者有关


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

惰性属性具有变异的getter,即访问惰性属性可能会变异结构。为什么?想想你第一次访问一个懒惰的属性。在第一次访问之前,该属性没有值,访问之后,将计算
=
右侧的表达式并将其指定给该属性。现在属性有了一个值。这就是访问惰性属性可以改变结构的方式。因此,您需要一个
var
来访问惰性属性

将结构更改为类也会有所帮助,因为现在
sqrs
存储引用,而
让sqrs
仅使对对象的引用保持不变,而不是使对象本身保持不变


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

谢谢您,Sweeper,您的清晰解释有助于我理解问题。因此,您的意思是,就
lazy
初始化而言,
lazy
属性被访问并分配给的那一刻,
struct
作为一个整体被改变,以至于它不能被分配给
struct
的常量引用,即使是第一次?如果是这样,语言的灵活性将是一个遗憾。无论如何,在现实生活中,安全性可能会压倒所有其他因素。