Иногда, изучая Python, можно наткнуться на вещи, которые позволяют решать задачи довольно неожиданным способом. К одной из таких вещей можно отнести унарный оператор ~, с помощью которого можно осуществить симметричную индексацию последовательности. Под симметричной индексацией последовательности будем подразумевать ее одновременный обход от начала и конца.
Индексация последовательностей
В языке Python достаточно удобно реализована индексация последовательностей. Мы легко можем обращаться к элементам последовательности от начала, используя индексы, начиная с нуля. То есть первый элемент последовательности имеет индекс 0, второй элемент последовательности имеет индекс 1 и так далее. В общем случае i-й элемент последовательности от начала имеет индекс i - 1.

Приведенный ниже код:
s = 'ПОКОЛЕНИЕ' print(s[0]) # первый символ строки print(s[1]) # второй символ строки print(s[7]) # предпоследний символ строки print(s[8]) # последний символ строки
выводит:
П О И Е
Обратите внимание: из-за того что нумерация начинается с нуля, а не с единицы, получается, что последним допустимым индексом является индекс на единицу меньший, чем длина последовательности.
Мы также легко можем обращаться к элементам последовательности от конца, используя отрицательные индексы, начиная с -1. То есть последний элемент (первый элемент с конца) последовательности имеет индекс -1, предпоследний элемент (второй элемент с конца) последовательности имеет индекс -2 и так далее. В общем случае i-й элемент последовательности от конца имеет индекс -i.

Приведенный ниже код:
s = 'ПОКОЛЕНИЕ' print(s[-1]) # последний символ строки print(s[-2]) # предпоследний символ строки print(s[-3]) # предпредпоследний символ строки
выводит:
Е И Н
Симметричная индексация и оператор ~
Как мы видим, в Python индексация от начала и от конца последовательности является несимметричной. Действительно, элементу с индексом 0 от начала соответствует симметричный элемент с индексом -1 от конца, элементу с индексом 1 от начала соответствует симметричный элемент с индексом -2 от конца и так далее.

Ниже приведена таблица соответствия индексов симметричных элементов от начала и конца последовательности.
Индекс от начала | Индекс от конца |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
В общем случае элементу с индексом i от начала будет соответствовать симметричный элемент с индексом -(i + 1) от конца.
Не все знают, но в Python для целых чисел можно использовать унарный оператор побитовой инверсии ~. Данный оператор "переворачивает" биты в двоичном представлении числа, выполняя операцию побитового отрицания: 1 преобразуется в 0, а 0 — в 1.
В общем виде оператор ~ преобразует целое число n в соответствии с правилом:
То есть положительные числа преобразуются в отрицательные со сдвигом на единицу.
Приведенный ниже код:
print(~0) print(~1) print(~2) print(~3) print(~-1) print(~-2) print(~-3) print(~-4)
выводит:
-1 -2 -3 -4 0 1 2 3
Несложно заметить, что мы можем использовать побитовую инверсию ~ для симметричной индексации от начала и конца последовательности в рамках одного цикла.
В качестве примера напишем функцию is_palindrome(), использующую симметричную индексацию с помощью оператора ~ для проверки входной строки на палиндром.
Приведенный ниже код:
def is_palindrome(s: str) -> bool: for i in range(len(s) // 2): if s[i] != s[~i]: return False return True print(is_palindrome('abccba')) print(is_palindrome('abcpba'))
выводит:
True False
По сути, выражение s[i] != s[~i] эквивалентно выражениям s[i] != s[-i - 1] и s[i] != s[len(s) - i - 1], однако является более красивым и компактным вариантом записи.
В качестве заключения. Скорее всего, вы нечасто будете использовать только что изученную возможность использования оператора ~ для реализации симметричной индексации. Однако знать такое будет совсем не лишним.
Присоединяйтесь к нашему телеграм-каналу, будет интересно и познавательно!
❤️Happy Pythoning!🐍