Swift for in - 获取条目作为参考

Swift for in - get entries as reference

有没有办法让 for .. in 循环 return references 到集合的条目而不是 copies?

假设我有一个包含 CGPoint 个对象的数组 points,我想遍历它们并将每个点传递给一个函数 adjustPoint,该函数可以使用 inout 参数。

现在执行以下操作不起作用,因为 for .. in 循环 return 点是不可变/可变的(取决于我是否使用 var数组中实际点的副本

for var point in points {
    adjustPoint(point: &point)  // This function only changes the copy
}

目前,我看到的唯一方法是遍历索引:

for i in 0..<points.count {
    adjustPoint(point: &points[i])
}

这真的是唯一的方法吗?还是用 for .. in 循环也可以?

注意: 我读过很久以前的这个问题(Swift 1 我相信)所以我想也许他们已经改变了一些东西同时:turn for in loops local variables into mutable variables

所以您原来的 for 循环问题的基本答案是:不。 for...in 旨在为您提供值类型的副本。正如您在评论中所说,这是一种强制函数式编程风格。

要改变数组,您必须以某种方式说 array[index],现在您指的是 original 值和 can 变异它。诀窍是找到一种可以防止常见错误的表达方式。我在下面提倡的四种技术是:

  1. 做一个强大的抽象作为 extension 这样你就可以在整个代码中 DRY
  2. 使用 indices 而不是手动范围,这也容易出错(... 对比 ..<
  3. 避免像 & 这样丑陋的 C 语言结构(参见 #1)
  4. 考虑保留变异版本和非变异版本

这可能最符合 Swift 的精神,即古怪、冗长且比您想要的更烦人,但最终非常具有表现力和强大的适当层次:

import Foundation
import CoreGraphics

protocol Pointy {
    var x: CGFloat { get set }
    var y: CGFloat { get set }
    func adjustBy(amount: CGFloat) -> CGPoint
    mutating func adjustInPlace(amount: CGFloat) -> Void
}

extension CGPoint: Pointy {
    func adjustBy(amount: CGFloat) -> CGPoint {
        return CGPoint(x: self.x + amount, y: self.y + amount)
    }

    mutating func adjustInPlace(amount: CGFloat) -> Void {
        x += amount
        y += amount
    }
}

extension Array where Element: Pointy {
    func adjustBy(amount: CGFloat) -> Array<Pointy> {
        return self.map { [=10=].adjustBy(amount: amount) }
    }

    mutating func adjustInPlace(amount: CGFloat) {
        for index in self.indices {
            // mysterious chunk of type calculus: need  "as! Element" -- https://forums.developer.apple.com/thread/62164
            self[index].adjustInPlace(amount: amount) // or self[index] = (self[index].adjustBy(amount: amount)) as! Element 
       }
    }
}


// Hide the above in a Util.swift that noone ever sees.

// AND NOW the true power shows
var points = [ CGPoint(x: 3.0, y: 4.0) ]
points.adjustInPlace(amount: 7.5)
points.forEach { print([=10=]) }
// outputs (10.5, 11.5)
let adjustedPoints = points.adjustBy(amount: 7.5) // Original unchanged