OGeek|极客世界-中国程序员成长平台

标题: ios - 在 Swift 中修改数组,(通过下标语法插入和附加) [打印本页]

作者: 菜鸟教程小白    时间: 2022-10-22 09:02
标题: ios - 在 Swift 中修改数组,(通过下标语法插入和附加)

所以我一直在用 Swift 的数组做一些实验。并使用以下代码发现了一些东西:

var shoppingList = ["Chocolate", "Bread", "Cheese", "Apple"]
println(shoppingList)
shoppingList[2..<2] = ["Butter", "Milk"] //Question #1 (Insert after 2nd element)
println(shoppingList)
shoppingList[0..<0] = ["Coffee"]
println(shoppingList)
shoppingList[7..<7] = ["Berry", "ie"] //Question #2 (Append at the end of array)
println(shoppingList)

给出以下输出:
[Chocolate, Bread, Cheese, Apple]
[Chocolate, Bread, Butter, Milk, Cheese, Apple]
[Coffee, Chocolate, Bread, Butter, Milk, Cheese, Apple]
[Coffee, Chocolate, Bread, Butter, Milk, Cheese, Apple, Berry, Pie]

问题#1:试验 subscript语法,似乎使用 ..<Insert到给定位置的数组的一个项目(在我的示例中通过 [2..<2] 位于第二个元素之后。与调用数组的 insert(_:atIndex 方法相反,这对 性能 是否有任何差异?或者取决于场景, 可读性 将只是使用 insert() 的好处?

问题#2:根据此处的 Swift 文档:Collection Type - See Modifying/Accessing Arrays

NOTE

You can’t use subscript syntax to append a new item to the end of an array.



然后我的问题是为什么我可以使用 [7..<7] 将 2 个元素“Berry”和“Pie”附加到数组的末尾?

在撰写本文时,我正在使用 Swift 1.2。



Best Answer-推荐答案


这看起来是一个很好的问题,所以我玩了一下。

看下标与7..<7的例子, 半开范围运算符 ..<正在创建 Range看起来像这样的结构:

let range = 7..<7
print(range.startIndex)    // 7
print(range.endIndex)      // 7

这也许是显而易见的。但是结束索引超出数组边界的范围对数组下标有什么影响?下标函数声明如下所示:
subscript (subRange: Range<Int>) -> ArraySlice<Element>

如果我们打电话
let shoppingList = ["Coffee", "Chocolate", "Bread", "Butter", "Milk", "Cheese", "Apple"]
let item = shoppingList[6..<7]
// item: ArraySlice<Int> = "Apple"

我们取回了一片“Apple”,正如您所期望的 - 我们已经要求了一片 shoppingList包含索引为 6 或更高且索引低于 7 的元素,因此我们返回一个包含索引 6 处的单个项目的切片。

还要注意这种行为:
let item = shoppingList[6...6]
print(range.startIndex)    // 6
print(range.endIndex)      // 7
endIndex实际上是 7 ,但是查看上面的下标,您不会期望任何问题,也不会得到任何问题。看待这个问题的一种方式是说 endIndex子范围的第一个数组元素的索引不是 要包含在数组切片中。我们可以假设下标实现对此进行了边界检查。

那么这个下标的 getter 的实现会是什么样的呢?让我们试试看:
extension Array {
    func myRangeSubscript(subRange: Range<Int>) -> ArraySlice<Element> {
        if count < subRange.endIndex { fatalError("Out of bounds!") } 

        var slice = ArraySlice<Element>()
        for i in subRange {
            slice.append(self[i])
        }
        return slice
    }
}

传入时7..<7 , 因为 x 不存在整数哪里7 >= x < 7 ,for 循环不会输入一次。这说明了为什么shoppingList[7..<7]有效,尽管乍一看似乎索引超出范围。

考虑下标赋值可能是什么样子只是一小步:
subscript(subRange: Range<Int>) -> ArraySlice<Element> {
    get {
        //...
    }
    set(newElements) {
        // newElements: Array<Element> = []
        self.removeRange(subRange)
        var elementIndex = 0
        for i in subRange {
            self.insert(newElements[elementIndex++], atIndex: i)
        }
    }
}

尽管文档另有说明,但您显然可以“附加”到数组末尾的原因可能是语义上您实际上并没有附加 - 您正在用新的元素数组替换数组中的一系列元素。碰巧的是 n..<n范围,你用你的新数组替换一个空的元素数组。从技术上讲,也许不是追加/插入,而是相当古怪的替换!因此,虽然文档在技术上可能是正确的,但实际上他们只是不考虑这种边缘情况。



简而言之,关于性能问题,我不能肯定地说下标是如何实际实现的。但很明显,使用此范围下标执行插入至少需要与使用 insert(_:atIndex 一样长的时间。或单个索引下标(无论如何它很可能调用 insert(_:atIndex),因为在准备 ArraySlice 时可能会有一些开销。创建,或调用 removeRange(_ .

出于这个原因,以及它的可读性较差并且没有在语义上反射(reflect)您要执行的操作的事实,我将避免使用此范围下标来插入数组项。只要坚持 insert(_:atIndex或相应的下标 - 这就是它们的用途!

关于ios - 在 Swift 中修改数组,(通过下标语法插入和附加),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31872443/






欢迎光临 OGeek|极客世界-中国程序员成长平台 (http://jike.in/) Powered by Discuz! X3.4