【问题标题】:Initialization problem with Swift struct that contains lazy initializers包含惰性初始化器的 Swift 结构的初始化问题
【发布时间】:2020-07-17 00:21:52
【问题描述】:

我为一个在线编程练习网站上的 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

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

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

【问题讨论】:

    标签: swift class struct lazy-initialization mutating-function


    【解决方案1】:

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

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

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

    【讨论】:

    • 谢谢你,Sweeper,你的解释清楚,这对我理解这件事很有帮助。因此,您的意思是,就lazy 初始化而言,在访问并分配lazy 属性的那一刻,整个struct 被更改,因此它不能分配给对@ 常量的引用987654330@ 还是第一次?如果是这样,语言的灵活性将是一个遗憾。无论如何,在现实生活中,安全可能会凌驾于所有其他因素之上。
    猜你喜欢
    • 2020-03-05
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多