Swift中的for in循环 - 获取条目的引用

11

有没有办法使 for .. in 循环返回集合中条目的引用而不是副本

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

现在做以下操作是行不通的,因为for .. in 循环将点作为不可变的/可变的(取决于我是否使用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

也许你可以尝试将它们封装在一个类中。 - dasdom
2
为什么不使用 map 呢?这样你会创建副本,但 points = points.map { adjustPoint(point: $0) } 将解决问题。 - jjatie
是的,我猜函数式编程方法是下一个最好的选择,并且保持代码整洁,但我仍然不喜欢Swift让正确处理引用变得如此繁琐,而是到处都有这些“一次性可用”的变量副本。虽然这可能只是个人偏好... - Keiwan
@jjatie 另外,如果我不想更新所有条目而只想更新最后的 n 个条目,有没有一种使用映射来实现这个目的的方法? - Keiwan
我认为这里对Swift数组有一个误解。你永远不会修改Swift数组,就像你给数字4加1时永远不会修改数字4一样。每次修改数组都会创建一个新的数组(就像将1添加到4会创建一个新的Int)。在某些情况下,编译器可以非常便宜地制作这个新副本(通过证明没有人能看到旧副本,因此只是在原地突变旧副本),但从逻辑上讲,在许多情况下,分配后的数组是与之前完全不同的数据结构。因此,不能引用单个元素。 - Rob Napier
显示剩余10条评论
1个回答

10
所以,对于你最初的 for 循环问题的基本答案是:不行。 for...in 设计为提供值类型的副本。就像你在评论中说的那样,这是一种强制性的函数式编程风格。
要改变数组,必须以某种方式使用 array[index],现在你正在引用原始值并且可以改变它。关键在于找到一种表现力强的方式,以防止常见错误。我提倡以下四种技术:
1. 制作强大的扩展抽象,使代码DRY。
2. 使用 indices 而不是手动范围,手动范围容易出错(... vs. ..<)
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 { $0.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($0) }
// outputs (10.5, 11.5)
let adjustedPoints = points.adjustBy(amount: 7.5) // Original unchanged

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接