Swift String.Index против преобразования строки в массив

В быстром документе они говорят, что используют String.Index для индексации строк, поскольку разные символы могут занимать разное количество памяти.

Но я видел много людей, превращающих строку в массив var a = Array(s) поэтому они могут индексировать по int вместо String.Index (что, безусловно, проще)

Поэтому я хотел проверить сам, одинаков ли он для всех символов Юникода:

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

И, конечно же, как уже упоминалось в строках документа, это просто массив символов, а затем кластер графем находится внутри объекта Character, так почему бы им не проиндексировать его с помощью int? какой смысл создавать / использовать String.Index на самом деле?

1 ответ

Решение

В String представление байтов упаковано, поэтому невозможно узнать, где находятся границы символов, не пройдя всю строку с самого начала.

При преобразовании в массив этот обход выполняется один раз, и в результате получается массив символов, которые расположены на равном расстоянии друг от друга в памяти, что позволяет подписке на постоянное время Int индекс. Важно отметить, что массив сохраняется, поэтому многие операции подписки могут быть выполнены на одном и том же массиве, требуя только одного обхода байтов строки для начальной распаковки.

Возможно расширение String индексом, который индексирует его Int, и вы видите, что это часто появляется на SO, но это не рекомендуется. Программисты стандартной библиотеки могли бы добавить его, но они намеренно решили этого не делать, потому что это скрывает тот факт, что каждая операция индексации требует отдельного обхода байтов строки, что O(string.count), Внезапно, безобидный код, подобный этому:

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

становится квадратичным

Другие вопросы по тегам