【问题标题】:Swift for in - get entries as referenceSwift for in - 获取条目作为参考
【发布时间】:2017-02-04 02:13:37
【问题描述】:

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

假设我有一个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

【问题讨论】:

  • 也许您可以尝试将它们包装在一个类中。
  • 为什么不使用地图?您将创建副本,但 points = points.map { adjustPoint(point: $0) } 会成功。
  • 是的,我想函数式方法是下一个最好的选择,并且可以保持干净,但我仍然不喜欢 Swift 让正确使用引用变得如此烦人的事实,相反,你拥有一切这些“一次性使用的一次性”可变副本随处可见。不过,这可能只是个人喜好......
  • @jjatie 另外,如果我不想更新所有内容,只更新最后 n 个条目。有没有办法使用地图来做到这一点?
  • 我认为这里对 Swift Arrays 存在误解。你从来没有真正修改过一个 Swift 数组,就像你在向它添加 1 时修改数字 4 一样。 Array 的每次修改都会创建一个新的 Array(就像将 1 加到 4 会创建一个新的 Int)。在某些情况下,编译器可能能够使新副本非常便宜(通过证明没有人可以看到旧副本,因此只需在适当的位置改变旧副本),但从逻辑上讲,在许多情况下实际上,Array after赋值是一种与之前完全不同的数据结构。所以不能引用单个元素。

标签: swift for-loop pass-by-reference


【解决方案1】:

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

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

  1. 将强大的抽象化为extension,以便您在整个代码中进行干燥
  2. 使用 indices 而不是手动范围,这也容易出错(.....&lt;
  3. 避免丑陋的倒退到像 &amp; 这样的 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

【讨论】:

    猜你喜欢
    • 2019-02-20
    • 2019-01-24
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 2018-02-13
    • 1970-01-01
    相关资源
    最近更新 更多