Свифт абстрагируется над строковыми индексами по нескольким причинам. Насколько я могу судить, основной целью является заставить людей остановиться , думая, что они просто целые числа. Под капотом они есть, но они ведут себя вопреки первоначальным ожиданиям людей.
ASCII в качестве "по умолчанию"
Наши ожидания в отношении строкового кодирования обычно довольно велики Engli sh -centri c. ASCII, как правило, является первым символом кодирования символов, которому обучают людей, и обычно с некоторым предлогом, что он каким-то образом является самым популярным или самым стандартным и т. Д. c.
Проблема в том, что большинство пользователей не американцы. Это западные европейцы, которым нужно много разных акцентов на своих латинских алфавитах, или восточные европейцы, которым нужны кириллицы c, или китайские пользователи, у которых есть куча разных символов ( свыше 74 000! , в которых они нуждаются чтобы иметь возможность писать. ASCII никогда не был международным стандартом для кодирования всех языков. Ассоциация Американских Стандартов создала ASCII для кодирования символов, относящихся к рынку США. Другие страны создали свои собственные кодировки для своих нужд.
Появление Unicode
Наличие региональных кодировок символов, пока международная связь с компьютерами не стала более распространенной. Эти фрагментированные кодировки символов не могли взаимодействовать друг с другом, вызывая все виды искаженного текста и пользователя путаница. Нужен был новый стандарт, чтобы унифицировать их и разрешить стандартизированное кодирование во всем мире.
Таким образом, Unicode был изобретен как единое кольцо для управления ими всеми. Одна кодовая таблица, продолжение объединение всех символов всех языков с достаточным пространством для дальнейшего расширения.
1 байт на символ
В ASCII существует 127 возможных символов. Каждый символ в строке кодируется как один 8-битный байт. Это означает, что для строки символов n
у вас есть ровно n
байтов. Подписка для получения i
-го символа - это вопрос простой арифметики указателей c, как и любого массива-subscripting. size_of_each_element
, равное всего 1 (байт), еще больше уменьшает до base_address + i
. Это было действительно быстро, и это сработало.
Это качество ASCII, равное 1 байт на символ, послужило основой для проектирования API типов строк во многих (большинстве?) Стандартных библиотеках языков программирования. Несмотря на то, что ASCII является неправильным выбором для кодировки «по умолчанию» (и существовал в течение десятилетий), к тому времени, когда Юникод стал повсеместным, ущерб был нанесен.
Кластеры расширенных графем
Какие пользователи воспринимать как символы называются «расширенные кластеры графем» в Unicode. Это базовый символ, за которым может следовать любое количество продолжающихся символов. Это разбило предположение «1 символ - 1 байт», на котором было построено много языков.
Думать, что символы являются байтами, - сломано в мире юникода. Не "о, это достаточно хорошо, мы будем беспокоиться об этом, когда мы расширимся на международные рынки", но абсолютно и полностью неработоспособно. Большинство пользователей не говорят по-английски sh. Пользователи Engli sh используют Emojis. Предположения, основанные на ASCII, просто больше не работают. Возьмите, например, Python 2.7, это прекрасно работает:
>>> s = "Hello, World!"
>>> print(s)
Hello, World!
>>> print(s[7])
W
И это не так:
>>> s = "????"
>>> print(s)
????
>>> print([2])
[2]
>>> print(s[2])
�
В Python 3 было введено критическое изменение: теперь индексы представляют кодовые точки, а не байты. Так что теперь приведенный выше код работает «как положено», печатая ?
. Но этого все еще недостаточно. Код с несколькими кодами все еще не работает, например:
>>> s = "A????Z"
>>> print(s[0])
A
>>> print(s[1])
?
>>> print(s[2]) # Zero width joiner
>>> print(s[3])
?
>>> print(s[4])
>>> print(s[5])
?
>>> print(s[6])
>>> print(s[7])
?
>>> print(s[8])
Z
>>> print(s[9]) # Last index
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
Swift обрабатывает это тривиально:
1> let s = "A????Z"
s: String = "A????Z"
2> s[s.index(s.startIndex, offsetBy: +0)]
$R0: Character = "A"
3> s[s.index(s.startIndex, offsetBy: +1)]
$R1: Character = "????"
4> s[s.index(s.startIndex, offsetBy: +2)]
$R2: Character = "Z"
Компромиссы
Медленная подписка по символам в Юникоде. Вы вынуждены пройтись по струне, начиная с самого начала, применяя правила нарушения графемы, когда вы go, считая, пока не достигнете желаемого значения. Это процесс O(n)
, в отличие от O(1)
в случае ASCII.
Если этот код был спрятан за оператором нижнего индекса, введите код:
for i in 0..<str.count {
print(str[i])
}
Может выглядеть как O(str.count)
(в конце концов, для l oop есть только один) ", верно ?!), но на самом деле это O(str.count^2)
, потому что каждая str[i]
операция скрывает линейный обход строки, что происходит снова и снова.
Swift String API
Строковый API Swift пытается заставить людей отказаться от прямой индексации и перейти к альтернативным шаблонам, не требующим ручной индексации, таким как:
String.prefix
/ String.suffix
для прерывания запуска или завершить строку, чтобы получить фрагмент - Использование
String.map
для преобразования всех символов в строке - и использование других встроенных функций для верхнего, нижнего, обратного хода, обрезки и т. д. c.
Строковый API Swift еще не полностью завершен. Существует большое желание / стремление улучшить его эргономику.
Однако, большая часть кода обработки строк, который люди привыкли писать, просто неверна. Возможно, они просто никогда не заметили, потому что они никогда не пытались использовать его на иностранном языке или с Emojis. Строка пытается быть правильной по умолчанию и затрудняет ошибки интернационализации.