【问题标题】:Swift Array instance method drop(at: Int)Swift Array 实例方法 drop(at: Int)
【发布时间】:2019-02-20 08:56:03
【问题描述】:

Array in Swift有几种排除元素的实例方法,如dropFirst()dropLast()drop(while:)等,那么drop(at:)呢?

注意:我会使用 remove(at:),但我正在使用的数组是 let 常量。

【问题讨论】:

  • 不是drop(where:)是drop(while:)
  • 哦,好眼力。谢谢你,@LeoDabus!固定的! :-)

标签: arrays swift slice sub-array


【解决方案1】:

注意:我会使用 remove(at:),但我正在使用的数组是一个常量。

我不会让这阻止你:

extension Array {
    func drop(at:Int) -> [Element] {
        var temp = self
        temp.remove(at: at)
        return temp
    }
}
let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2)) // [1, 2, 4, 5]

【讨论】:

  • 有趣。谢谢!是的,这实际上是我最初的方法。看着你的代码,我突然想到:你的drop(at:) 方法返回的数组是可变的,因为temp 是一个变量?也就是说,我可以做numbers.drop(at: 2).remove(at: 1)吗?可能不是一个很好的例子,但你明白我的意思。我只是好奇。我想我可以试试看。 :-)
  • 好的。我只是将您的扩展名复制并粘贴到Swift REPL 并尝试numbers.drop(at: 2).remove(at: 1) 并返回error: cannot use mutating member on immutable value: function call returns immutable value。所以,我猜你的drop(at:) 方法返回的数组是不可变的,即使temp 是一个变量。
  • 1) 这不会像标准库中的其他 drop 函数那样返回 ArraySlice2) 这是低效的,因为它在内存中创建了一个全新的数组
  • @Carpsen90 我在问题中看不到这样的要求。我在这里唯一回应的是我引用的那句话,“我会使用remove(at:),但我正在使用的数组是一个常量”。好吧,remove(at:) 不会返回切片,它会在内存中创建一个全新的数组。所以我在这里的回答就是它所做的,另外它还展示了如何处理作为常量的原始数组。
【解决方案2】:

您可以扩展RangeReplaceableCollection 协议而不是Array 类型,这样您也可以在字符串上使用它:

extension RangeReplaceableCollection {
    func drop(at offset: Int) -> SubSequence {
        let index = self.index(startIndex, offsetBy: offset, limitedBy: endIndex) ?? endIndex
        let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        return self[..<index] + self[next...]
    }
}

var str = "Hello, playground"
str.drop(at: 5)  // "Hello playground"

let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2))  // "[1, 2, 4, 5]\n"

如果你也想在你的方法中接受 String.Index:

extension RangeReplaceableCollection {
    func drop(at index: Index) -> SubSequence {
        let index = self.index(startIndex, offsetBy: distance(from: startIndex, to: index), limitedBy: endIndex) ?? endIndex
        let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        return self[..<index] + self[next...]
    }
}

var str = "Hello, playground"
str.drop(at: 0)               // "ello, playground"
str.drop(at: str.startIndex)  // "ello, playground"

【讨论】:

  • 好主意!但是,我会做两个单独的扩展(一个用于String,一个用于Array),每个都有自己的drop(at:) 实例方法。为什么?因为这就是remove(at:) 在 Swift 中的实现方式。它为每个结构单独定义(String & Array)。此外,remove(at:) for String 采用 String.Index,而不是 Int。所以,我会让drop(at:) 也使用String.Index,而不是Int,以保持一致。
  • 无论如何,干得好!我认为他们应该考虑将我们的实例方法添加到Swift standard library
  • 不错!谢谢你。 :-)
【解决方案3】:

我要添加到您的实现中的唯一内容是带有有用错误消息的保护语句:

extension Array {
    func drop(at index: Int) -> ArraySlice<Element> {
        guard indices.contains(index) else {
            fatalError("Index out of range")
        }
        return self[0..<index] + self[(index+1)..<endIndex]
    }

    func drop(range: CountableRange<Int>) -> ArraySlice<Element> {
        guard (indices.lowerBound, range.upperBound) <= (range.lowerBound, indices.upperBound) else {
            fatalError("Range is out of the indices bounds")
        }
        return self[0..<range.lowerBound] + self[range.upperBound..<endIndex]
    }
}

let a = [1,2,3]
a.drop(at: 3)          //Fatal error: Index out of range
let b = [0,1,2,3,4,5]
b.drop(range: 1..<5)   //[0, 5]

【讨论】:

  • 很好的电话。我刚刚找到了source code for `remove(at:) for Array,它也符合您的建议。
  • @ma11hew28 this 你在找什么?还是要遵循排除函数以ArraySlice 作为返回值的方式?
  • 谢谢。我不知道removeSubrange(_:)。很酷!好吧,我只想删除一个元素,而不是元素的子范围。而且,removeSubrange(_:) 不适用于常量。例如,如果我做numbers.removeSubrange(2...2),我会得到error: cannot use mutating member on immutable value: 'numbers' is a 'let' constant。是的,我确实希望 ArraySlice 作为返回值。你的drop(at:) 函数正是我想要的。
  • 感谢您建议添加 index-out-of-range 错误消息。我也刚刚将它添加到我的答案中。
  • @ma11hew28 我刚刚发现了一个奇怪的 drop 行为:var numbers: [Int] = []; numbers.drop(at: -4) 返回 ArraySlice([]) 没有任何错误消息,可能是因为它不是像 remove(at:) 这样的变异函数
【解决方案4】:

return self[0..&lt;index] + self[index+1..&lt;endIndex]

丑陋。为什么不使用您提供的工具?

extension Array {
    func drop(at:Int) -> ArraySlice<Element> {
        return prefix(upTo: at) + suffix(from: at+1)
    }
}
let arr = [1,2,3,4,5]
let slice = arr.drop(at:2) // [1,2,4,5]

编辑似乎苹果更希望你说(使用部分范围)

return self[..<at] + self[(at+1)...]

但我个人认为这更丑陋,毕竟我建议的所有方法都没有被弃用。

【讨论】:

【解决方案5】:

使用Swift extensiondrop(at:) 添加到Array 结构怎么样?

extension Array {
  func drop(at index: Int) -> ArraySlice<Element> {
    precondition(indices.contains(index), "Index out of range")
    return self[..<index] + self[(index+1)...]
  }
}

它返回原始数组的切片,没有指定索引处的元素。

let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2))
// Prints "[1, 2, 4, 5]"

注意:您可能还想将drop(at:) 添加到ArraySlice

【讨论】:

  • 这对我来说很完美。非常小的改进:precondition(index &lt; endIndex &amp;&amp; index &gt;= startIndex, "Index out of range") 使用 short-circuit evaluation,因为它通常是第一个失败的条件
  • return prefix(upTo: index) + suffix(from: index+1) 看起来更干净 IMO。
  • @Carpsen90,谢谢。好点。我最初写了index &gt;= startIndex &amp;&amp; index &lt; endIndex,但我不确定我是否记得我为什么颠倒了比较的顺序,所以也许我还想添加评论。我认为写startIndex &lt;= index &amp;&amp; index &lt; endIndex 更加清晰和语义化。虽然,也许它没有那么高性能,但只有在它即将崩溃时才会发生性能冲击。哈哈。最终,我想我会像您在回答中所做的那样使用indices.contains(index)。一开始我对它并不熟悉,但它简单易读,而且看起来足够快。
  • @matt 我听到了,尤其是在下标符号需要使用self 的实例函数内部。无论您喜欢哪个。我想我会坚持使用下标符号来遵循文档中的建议并保持一致性,因为这可能是我将在其他地方使用的。而且,将范围(我之前的)更改为部分范围使它看起来更好一些,所以谢谢你。 :-)
  • 感谢@Carpsen90 和@matt,感谢您提供出色的反馈和建议。感谢你们两位对细节、清晰度和性能的关注。我学到了很多东西,现在,我对drop(at:) 有了更好的实现。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
相关资源
最近更新 更多