Swift字符串和整数下标

4

这其实是一个理论问题。

为什么以下内容(类似的还有更多)不是Swift标准库的一部分?有哪些不好的情况

extension String {
    subscript(_ range: Range<Int>) -> Substring {
        return self[index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)]
    }
}

extension Substring {
    subscript(_ range: Range<Int>) -> Substring {
        return self[index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)]
    }
}

5
你把一个O(n)的操作隐藏在看似O(1)的下标操作里。 - Hamish
@hamish,就像我在下面的答案评论中所说的那样 - 原因是为了保持下标复杂性明显,对吧? - simpleone
1个回答

10
为了补充Hamish的观点,这将会产生非常令人惊讶的行为。让我们考虑更简单(但等效)的string[int]情况,这将建立在其上。为什么不用int来进行字符串下标呢?
字符串是一系列字符,而不是字节。它们可以存储在UTF-8中,这意味着你不知道一个字符有多大,也不能跳到任意位置。为了找出“第100个字符”,您必须从开头开始解码所有内容。这是O(n)。
因此,您需要编写像这样的代码,这感觉非常“安全”:
for index in 0..<string.count {
    print(string[index])
}

但是秘密地,这个算法的时间复杂度是O(n^2),这真的很令人惊讶,因为它看起来像是O(n)。你可能会说“我的字符串只有20个字符,谁在乎呢”,但我们使用字符串进行许多操作,包括多兆字节的NSTextStorage。(而且这在Swift中会显著扩展,因为Swift包含了通用算法,它们的性能承诺依赖于下标操作的时间复杂度是O(1)。)

所以我可以为你做事情变得“简单”,直到突然遇到需要关注性能的时候,一切都变得非常困难。(我指的不是“超级快”的“性能”,我是指“性能”不会使整个应用程序停止数分钟。)

具有“简单”索引的语言之所以“简单”,是因为它们忽略了Unicode。因此,一切都很好,直到你碰到表情符号,然后一切都爆炸了,你必须编写疯狂的肮脏代码来解决它。Swift决定像表情符号(以及中文、阿拉伯语、泰语等)这样的东西是放在字符串中非常正常的东西,因此应该作为一流的元素来处理,而不是当事情爆炸时绕过去。

如果你想学习一个走另一条路的语言,请看看Go,它有一个用于字符串的“符文”系统,一旦出现表情符号就非常难处理。Go的答案是“程序员在可能有多字节字符的情况下应该做正确的事情。” Swift的答案是“语言应该始终做正确的事情。”两者都有合理的论据,但Swift的方法避免了许多现实世界中的错误(比如我在ObjC中意外截断多字节密码时给我的中国用户带来的可怕安全问题)。


这是一篇非常出色的回答,对后代有用...我是认真的!我很清楚Unicode存储的工作原理。我知道bytesof(substring(x, n, m))的成本很高。尽管substring操作很常见,但str[index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)]有点繁琐。提供整数下标的主要原因是否只是复杂性显而易见?(“是”我认为是一个好答案) - simpleone
你所说的关于“程序员”和“语言”的观点非常正确……另一方面,Swift 是纯粹主义的,可能最终会有一个名为“SmartString”的广泛使用的 pod,被许多人使用,并提供所有那些“糟糕”的东西。有人可能会说——这不是语言的错……另一方面,当存在某些隐含的复杂性时,人们会寻求简单的解决方案。这是语言设计的问题。我没有一个好的解决方案……比如警告“您正在使用潜在昂贵的下标,请用括号将其静音”?;) - simpleone
“用括号将其静音” 的问题在于您可能没有直接使用下标。它可能是通用算法的一部分。由于字符串是集合,像 first(where:)joined() 这样的函数可能是通用的。编译器如何警告您这些是 O(n^2)(或更糟)?您该如何消除它?相反,您应该通常问自己“为什么要按整数切片?” 如果不是“因为我正在编写固定长度格式的解析器”,请问自己是否真的选择了正确的工具,而不是创建一个执行您想要的操作的专门扩展方法。 - Rob Napier
“括号”只是一个玩笑而已 :) 但你懂我的意思。最让人烦恼的部分就是处理索引,做一些类似于格式化 1234567890 -> 123-456-7890 的小工作。但我得出结论,O(1)的承诺比程序员的懒惰更重要。 - simpleone
1
当然,带有一些不错的语法糖的正则表达式。 - simpleone
显示剩余3条评论

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