Иногда, изучая 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!🐍