微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Swift 数组 reversed()[n] 是否有效?

如何解决Swift 数组 reversed()[n] 是否有效?

当您在 Swift 中对数组调用 reversed() 时,您会得到一个 ReverseCollection,它仅使用反向访问包装原始数组。因此这是非常有效的:

let arr = [1,2,3,4]
for i in arr.reversed() { print(i) }

除了访问之外,实际上没有任何东西被逆转;这里 reversed 的时间复杂度是 O(1)。酷!

但是当我用一个整数索引 reversed() 并查看 Quick Help 时,似乎我已经失去了所有的效率;我看到了生成新数组的 Sequence reversed()

let arr = [1,4]
let i = arr.reversed()[1] // ???? this is a different `reversed()`!

这似乎是真的,因为 reversed() 数组本身不支持按数字索引:

let arr = [1,4]
let rev = arr.reversed()
let i = rev[1] // compile error!

所以我的问题是:在我的第二个示例中按数字索引到 reversed() 数组中真的会失去 ReverseCollection 索引反转的效率吗?

解决方法

是的,通过 Int 进行索引会导致您失去对反向数组的 O(1) 访问权限。相当有问题!

正如您所注意到的,这里的 reversed() 是一个重载方法;特别是在 Array 上,您有两个定义可供选择:

  1. BidirectionalCollection.reversed(),返回一个 ReversedCollection,以及
  2. Sequence.reversed(),将任何序列转换为反转的 [Element]

这里的重载对于 Array 本身来说是最令人困惑的,因为它是唯一一个 Sequence 类型的 type(of: x) == type(of: x.reversed())

Swift 类型检查器更喜欢更具体的重载而不是不太具体的重载,因此一般来说,编译器会尽可能使用 BidirectionalCollection 重载而不是 Sequence 重载。问题:BidirectionalCollection 有一个 opaque index type,不能使用 Int 建立索引;当您使用 Int 索引到集合中时,编译器会被迫选择 Sequence 重载而不是 BidirectionalCollection 重载。这也是您的第二个代码示例无法编译的原因:Swift 代码推理不考虑其他行上的周围上下文;就其本身而言,rev 首选为 ReversedCollection<Array<Int>>,因此尝试使用 Int 对其进行索引会失败。

您可以通过以下方式更清楚地看到这一点:

func collType1<T: Collection>(_: T) {
    print(T.self) // ReversedCollection<Array<Int>>
    print(T.Index.self) // Index
}

func collType2<T: Collection>(_: T) where T.Index == Int {
    print(T.self) // Array<Int>
    print(T.Index.self) // Int
}

let x: [Int] = [1,2,3]
collType1(x.reversed())
collType2(x.reversed())

当基于 Int 的索引似乎没有任何其他副作用时,为了避免您怀疑编译器是否可以对此进行优化,在撰写本文时,答案似乎是否定的。 Godbolt output 有点太长,无法在这里重现,但目前,比较

func foo1(_ array: [Int]) {
    if array.reversed()[100] > 42 {
        print("Wow!")
    }
}

func foo2(_ array: [Int]) {
    if array.reversed().dropFirst(100).first! > 42 {
        print("Wow!")
    }
}

启用优化后显示 foo2 执行直接数组访问

cmp     qword ptr [rdi + 8*rax + 24],43

完全优化掉了 ReversedCollection 包装器,而 foo1 经历了明显更多的间接性。

,

Ferber 很好地解释了原因。

这是一个临时解决方案(可能不是每个人都喜欢,因为我们是从标准库扩展类型):

// RandomAccessCollection ensures fast index creation
extension ReversedCollection where Base: RandomAccessCollection {
  subscript(_ offset: Int) -> Element {
    let index = index(startIndex,offsetBy: offset)
    return self[index]
  }
}

[1,3].reversed()[0] // 3

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?