Нотация o(...)
Нотация O(…) используется в теории сложности алгоритмов для описания эффективности и потребления памяти конкретными алгоритмами. В большинстве случаев в скобках указываются значения в пропорциях к n – числу обрабатываемых элементов или длине обрабатываемого элемента. В скобках также может указываться мера потребления памяти или время обработки. Запись O(1) означает постоянное время, то есть время обработки не зависит от величины n. Запись O(log n) означает увеличение времени по логарифмическому закону – это очень быстрый алгоритм, время работы которого пропорционально log n. Запись O(n) означает линейное увеличение времени – это довольно быстрый алгоритм, время работы которого пропорционально n. Запись O(n2) означает увеличение времени по квадратичному закону – это медленный алгоритм, время работы которого пропорционально n2. Запись O(nm) означает увеличение времени по полиномиальному закону – скорость работы такого алгоритма падает очень быстро с ростом n, особенно при значениях m ≥ 3. Запись O(n!) означает увеличение времени по факториальному закону – даже при маленьких значениях n такой алгоритм становится слишком медленным, чтобы иметь практическую ценность.
Спецификаторы формата %-2d, %U, %c и % X описываются в таблице 3.4. Как будет показано далее, спецификатор %X используется для вывода целых чисел в шестнадцатеричном виде, а когда он применяется к значению []byte, выводится последовательность чисел, состоящих из двух шестнадцатеричных цифр, по одному на каждый байт. Наличие пробела в спецификаторе указывает, что байты должны выводиться через пробел.
Индексирование строк
Отсчет индексов, то есть позиций байтов UTF-8 в строке, начинается с 0 и продолжается до значения, определяющего длину строки минус единицу. Также имеется возможность индексирования в обратном направлении – с конца строки, с использованием индексов со значениями len(s) - n, где n – количество байтов, отсчитываемых с конца. Например, для выражения s := "clár", на рис. 3.2 показана строка s в виде последовательностей символов Юникода, кодовых пунктов и байтов, а также приводятся несколько допустимых индексов и пара срезов.
Для доступа к каждой позиции в строке, изображенной на рис. 3.2, можно использовать оператор индексирования [] , который возвратит соответствующий ASCII-символ (как значение типа byte).
Например, s[0] == ‘c’, а s[len(s) - 1] == ‘r’. Первый байт последовательности, соответствующей символу ‘á’, имеет индекс 2, но, если обратиться к элементу строки s[2], программа получит только первый байт (0xC3) символа ‘á’ в кодировке UTF-8.
c |
l |
á |
r |
Символы |
|
U+0063 |
U+006C |
U+00E1 |
U+0072 |
Кодовые пункты |
|
0x63 |
0x6C |
0xC3 |
0xA1 |
0x72 |
Байты |
0 |
1 |
2 |
3 len(s) - 2 |
4 len(s) - 1 |
Индексы |
Рисунок 3.2 Строение строки
Для строк, содержащих только 7-битные ASCII-символы, первый символ (в виде значения типа byte) можно извлечь с помощью выражения s[0], а последний – с помощью выражения s[len(s) - 1]. Однако в общем случае для извлечения первого символа (в виде значения типа rune, содержащего все байты UTF-8, представляющие символ) следует использовать функцию utf8. DecodeRuneInString(), а для извлечения последнего символа – функцию utf8.DecodeLastRuneInString().
Для доступа к отдельным символам имеется несколько возможностей. Для строк, содержащих только 7-битные ASCII-символы, можно использовать обычный оператор индексирования [], обеспечивающий очень быстрый (O(1)) доступ. В случае с другими строками можно преобразовать строку в значение типа []rune и использовать оператор индексирования [] с этим значением. В этом случае индексирование тоже выполняется очень быстро (O(1)), но сама операция преобразования является достаточно дорогостоящей, с точки зрения производительности и потребления памяти (O(n)).
Для произвольных строк (то есть для строк, которые могут содержать неASCII-символы), обычная операция индексирования далеко не всегда дает желаемый результат.
