Swift中数组的负索引是什么?

7

有没有可能覆盖Swift的默认数组下标,使其像Python一样处理负数索引?

例如,a[-1] 应返回 a 的最后一个元素,a[-2] 返回它之前的元素。

通过扩展Array类型应该可以实现这一点,但是下面的代码不起作用,因为它将无限循环:

extension Array {
   subscript (index:Int) -> [Element] {
        return (index < 0) ? self[self.count+index] : self[index]
   }
}

覆盖如此基础的东西会有多糟糕?

3
我个人认为那是一个很糟糕的想法。这会在以后重复使用/维护你的代码时容易让毫不知情的开发者犯错误。 - onnoweb
1
最简单的公式实际上是:unsignedIndex = signedIndex % arrayCount,即 return self[signedIndex % count]。但这种方式索引永远不会溢出,所以要小心。 - SirEnder
3个回答

18

实际上,有一种相对良好的做法是使用Swift中的标签参数。

extension CollectionType where Index : BidirectionalIndexType {
  subscript(back i: Index.Distance) -> Generator.Element {
    return self[endIndex.advancedBy(-i)]
  }
}

let ar = [1, 2, 3, 4]
ar[back: 1] // 4
ar[back: 2] // 3

你可以很容易地更改语义。例如,此实现要求索引大于0。将其更改为0返回最后一个元素就像这样简单:self[endIndex.predecessor().advancedBy(-i)],或者,如果您希望假设索引为负数,则使用:self[endIndex.advancedBy(-i)]

标记参数的优点是清晰明了,并且没有人会意外使用它。


7

Swift 4版本:

extension Collection where Index: Comparable {
    subscript(back i: Int) -> Iterator.Element {
        let backBy = i + 1
        return self[self.index(self.endIndex, offsetBy: -backBy)]
    }
}

这段代码存在一些问题,我已经进行了修正。实际上它并不依赖于 IndexComparable,所以我移除了这个限制。现在它扩展了 BidirectionalCollection,这保证了我们可以向后迭代。而且增加偏移量 1 并不是必要的;这会导致我们越过目标(-1 应该是最后一个元素,而不是倒数第二个)。 - rgov

1

Swift 5.7

@Hristo,我们不仅可以在Swift下标功能中使用负索引,还可以实现错误处理来控制是否“越界”。因此,请使用以下代码。

let array: [Int] = [199, 288, 377, 455, 533, 622, 711]

enum SubscriptError: Error {
    case greaterThanZero
    case lessThanLastIndex
}

extension Collection {

    public subscript(negative i: Int) -> () throws -> Element {
        
        let backward = i - 1
        
        if i > 0 {
            return { throw SubscriptError.greaterThanZero }
        }
        if i < -1 * ((endIndex as! Int) - 1) {
            print(endIndex)
            return { throw SubscriptError.lessThanLastIndex }
        }           
        return { self[index(endIndex, offsetBy: backward)] }
    }
}

do {
    try array[negative: -6]()                      // 199
} catch {
   print("It's \(error)")
}

以下是结果:
let array: [Int] = [199, 288, 377, 455, 533, 622, 711]

try array[negative: 2]()       //  "It's greaterThanZero"
try array[negative: 1]()       //  "It's greaterThanZero"
try array[negative: 0]()       //  711
try array[negative: -1]()      //  622
try array[negative: -2]()      //  533
try array[negative: -3]()      //  455
try array[negative: -4]()      //  377
try array[negative: -5]()      //  288
try array[negative: -6]()      //  199
try array[negative: -7]()      //  "It's lessThanLastIndex"
try array[negative: -8]()      //  "It's lessThanLastIndex"

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