1

在 swift 文档中,他们说他们使用 String.Index 来索引字符串,因为不同的字符可以占用不同的内存量。

但是我看到很多人将 String 转换为数组var a = Array(s),以便他们可以通过 int 而不是 String.Index 进行索引(这绝对更容易)

所以我想自己测试一下它是否对所有 unicode 字符都完全相同:

let cafeA = "caf\u{E9}" // eAcute
let cafeB = "caf\u{65}\u{301}" // combinedEAcute

let arrayCafeA = Array(cafeA)
let arrayCafeB = Array(cafeB)

print("\(cafeA) is \(cafeA.count) character \(arrayCafeA.count)")
print("\(cafeB) is \(cafeB.count) character \(arrayCafeB.count)")
print(cafeA == cafeB)

print("- A scalar")
for scalar in cafeA.unicodeScalars {
    print(scalar.value)
}
print("- B scalar")
for scalar in cafeB.unicodeScalars {
    print(scalar.value)
}

这是输出:

café is 4 character 4
café is 4 character 4
true
- A scalar
99
97
102
233
- B scalar
99
97
102
101
769

果然,正如文档中提到的那样,字符串只是一个字符数组,然后字素簇在字符对象中向下,那么他们为什么不通过 int 对其进行索引呢?实际上创建/使用 String.Index 有什么意义?

4

1 回答 1

2

在字符串中,字节表示是打包的,因此如果不从头开始遍历整个字符串,就无法知道字符边界在哪里。

当转换为数组时,这是一次遍历,结果是在内存中等距分布的字符数组,这就是允许通过Int索引进行恒定时间下标的原因。重要的是,数组被保留了,因此可以对同一个数组进行许多下标操作,只需要遍历一次字符串的字节,就可以进行初始解包。

可以使用一个下标来扩展 String ,该下标通过一个 对其进行索引Int,并且您会看到它经常出现在 SO 上,但这是不建议的。标准库程序员本可以添加它,但他们故意选择不添加,因为它掩盖了每个索引操作都需要单独遍历字符串字节的事实,即O(string.count). 突然之间,像这样无害的代码:

for i in string.indices {
    print(string[i]) // Looks O(1), but is actually O(string.count)!
}

变成二次元。

于 2018-01-18T16:54:03.813 回答