Источник: Nuances of Programming
Прежде всего нужно понять «Истину», как она представлена в таблице истинности. На самом деле таких таблиц несколько, но для этого урока нам хватит двух — AND и OR. Начнём.
AND
Обозначается как &. В этом случае сравнение двух битов, A и B, даст результат 1 только тогда, когда оба A и B равны 1.
С помощью приведённой выше таблицы легко определить результат побитовой операции AND между двумя целыми числами. Например, чтобы найти результат 12 & 4, преобразуйте оба числа в их двоичные представления:
- 12 = 1100
- 4 = 100
Так-так. 12 имеет четыре бита, а 4 всего три бита. В таком случае можно сделать их одинаковой длины, добавив 0 туда, где меньше битов. 4 = 0100.
Теперь, когда числа одинаковой длинны, мы можем произвести операцию:
1100 &0100 =
0100
Начиная с самого правого бита каждого числа, сверяясь с таблицей выше, найдите все значения. Итак, у нас получается 0&0=0, 0&0=0, 1&1=1 и 1&0=0. Результат 0100, т.е. 4, значит 12&4 = 4.
OR
Обозначается как |. Здесь, сравнение двух битов, A и B, даст результат 1, если хотя бы один бит (A или B) равен 1.
С помощью этой таблицы можно определить результат побитовой операции OR между двумя целыми числами.
Например, чтобы найти результат 13|3, снова преобразуем целые числа в двоичное представление:
- 13 = 1101
- 3 = 11
Добавляем нули: 3 = 0011.
Вычисляем OR:
1101 |0011 =1111
Сопоставляем каждый бит, начиная справа, с таблицей выше, чтобы найти соответствующее значение. Итак, у нас получается 1|1=1, 0|1=1, 1|0=1 и 1|0=1. Результат 1111, т.е. 15, значит 13|3=15.
Чем это может быть полезно?
Представьте, что вам нужно написать функцию, которая принимает строку и возвращает строку декоратор в окружении какого-либо из следующих тегов: <i>, <b>, и <u>.
Вариант 1
Мы можем написать функцию, которая учитывает все теги в своей подписи. Тогда у нас получится что-то вроде этого:
Данная функция работает хорошо. Сложность этого варианта заключается в том, что вы должны запомнить расположение каждого тега при передаче аргументов в функцию.
Кроме того, предположим, что вы просто хотите подчеркнуть текст. Вам придётся сделать что-то вроде этого: decorateString("Hello", false, false, true). Любой, кто будет читать этот код (кроме вас), должен будет найти функцию и посмотреть, что в ней, чтобы узнать, что является false, а что true.
Вариант 2
Вообще-то вам не нужно писать функцию таким способом в Javascript. Вы можете сделать второй аргумент объектом и деструктурировать его, чтобы получить доступ к значениям и работать с ними. Вот так:
Так намного лучше, так как мне не нужно запоминать порядок тегов, а только указывать те, которые я хочу. Но я не использую Javascript. Есть ещё варианты?
Вариант 3
Вариант есть. Давайте представим, что бит представляет собой флаг. Когда бит равен 1, флаг поднимается, когда бит равен 0, флаг опускается. Теперь у каждого тега будет флаг в определенной позиции. Мы можем определить каждый из тегов следующим образом (в двоичном формате):
includeITag = 001 // 1
includeBTag = 010 // 2
includeUTag = 100 // 4
Вызов функции будет выглядеть так:
- Чтобы включить только тег I: decorateString("Hello", 1)
- Чтобы включить только тег B: decorateString("Hello", 2)
- Чтобы включить только тег U: decorateString("Hello", 4)
Неплохо, но мне придётся запоминать эти цифры. Верно?
На самом деле нет. Эти значения будут определены как константы в классе/файле, который содержит эту функцию, поэтому вам не нужно их запоминать.
Но нет никакого способа использовать два тега вместе. Чтобы строка включала теги I и B, нам нужно установить два флага, т.е. 011. А чтобы включить три тега, нам нужно 111.
Т.е. нам нужно определять значения для всех возможных комбинаций тегов? Нет. Вы создаёте только те, которые вам нужны, и генерируете комбинации. В этом нам пригодится оператор |.
includeITag | // 001
includeBTag // 010
// 011
Теперь мы можем вызывать функцию так:
Чтобы включить только I и B теги: decorateString("Hello", includeITag | includeBTag)
Чтобы включить три тега: decorateString("Hello", includeITag | includeBTag | includeUTag)
Использовать можно в любом порядке.
Как же тогда извлечь эти значения из входных данных? Проверяя, установлен ли флаг! В этом нам поможет оператор &.
Чтобы проверить, установлен ли бит в индекс i, нужно проверить значение индекса 2^i через AND. Если результат не равен 0, то бит установлен, иначе бит не установлен.
Например, нужно проверить 1101, установлен ли бит с индексом 1: 2¹=2. Получается:
1101 &
0010 =
0000
Так как это 0, бит в индексе 1 не установлен. А если так же проверить 1011:
1011 &
0010 =
0010
Так как результат не равен 0, значит бит в индексе 1 установлен. Теперь мы можем создать такую функцию:
Это можно реализовать в любом языке программирования, без деструктуризации или передачи длинного списка параметров. Пример использования такого подхода — установка флагов при запуске нового Activity в Android:
myIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION|Intent.FLAG_ACTIVITY_CLEAR_TOP);
Читайте нас в телеграмме и vk
Перевод статьи Ekundayo Blessing Funminiyi: Bit Manipulation — Playing With the Truth