如何在for循环中安全地从数组中删除元素?

13

充分披露,这是一个家庭作业问题:

应该有一个类型为[Circle]的私有属性。一个圆的数组。 该方法应删除任何半径大于最小要求且小于最大要求的圆。

似乎明显应该使用removeAtIndex()来删除不符合循环中确定条件的数组项。然而,许多人之前指出了在循环中删除项目的危险,我猜可能是“迭代器/索引不匹配”的问题。

最终,我创建了一个空数组,并使用.append()将满足“好”条件的值推送到filteredCircles数组中,但我无法避免地感觉这不符合任务的标准。

是否有一种解决方案可以在循环中实际删除数组项?


2
从最后一个索引开始循环,直到达到第一个。删除项目仅会影响其后面的项目索引。 - vacawama
@vacawama 我该如何在Swift中实现这个?循环语法不允许像其他语言一样使用index--,所以我不确定如何倒数计数。 - nipponese
@vacawama 嗯...刚刚学会了circles.enumerate().reverse()。谢谢! - nipponese
2个回答

14
如果在引用的文本中没有明确要求必须使用FOR LOOP(我没有看到这个要求),那么应该使用filter方法。 当你在一个数组上调用filter时,你会得到一个新数组,其中只包含符合你传递给filter的闭包的值。原始数组不会被改变。
struct Circle {
    let radius: Double
}

let circles = [Circle(radius: 1), Circle(radius: 5.1), Circle(radius: 4), Circle(radius: 10.8)]

let bigCircles = circles.filter { $0.radius > 5 }

这种方法为什么比在for循环中更改数组要好

  1. 由于circles是一个常量,你不会遇到与多线程编程相关的问题。如果circles是可变的,那么其他线程可能会在你循环时更改它,从而产生非常可怕的副作用。
  2. 它更少出错。你没有写明CPU应该执行什么,而是描述了结果应该如何。因此,你和编译器之间的潜在误解就少了 :)
  3. 你写的代码更少,这意味着错误的可能性更小。

这些都是编写函数式编程代码的一些好处。


我想我应该试一下,但是.filter实际上会修改数组吗? - nipponese
1
不是这样的。这就是为什么他将返回值分配给圆变量(必须使用“var”声明)。 - Marcus Rossel
1
@nipponese:不,它不会。这是一件好事,因为(出于几个原因)“circles”应该是一个常量。但是如果你想的话,你可以将“circle”变成一个“var”,并将结果重新分配给同一个变量。 - Luca Angeletti

1
进一步解释@vacawama的答案:
struct Circle {
    var radius: Int
}

struct MyStruct {
    private var circles: [Circle]

    mutating func removeCirclesWithRadiusWithin(range: Range<Int>) {
        for index in (circles.startIndex..<circles.endIndex).reverse() {
            if range.contains(circles[index].radius) {
                circles.removeAtIndex(index)
            }
        }
    }
}

如果您想在Circle半径(radius)中使用Double,但仍希望保持良好的语法:
struct Circle {
    var radius: Double
}

struct MyStruct {
    private var circles: [Circle]

    mutating func removeCirclesWithRadiusWithin<I: IntervalType where I.Bound == Double>(interval: I) {
        for index in (circles.startIndex..<circles.endIndex).reverse() {
            if interval.contains(circles[index].radius) {
                circles.removeAtIndex(index)
            }
        }
    }
}

因为您正在更改结构体中存储属性的内容,所以如果您使用let声明了MyStruct的实例,则无法使用此方法。 - Marcus Rossel

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