Заметки
Когда программисту скучно

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика


Предисловие

Мне была поставлена задача сделать реестр для объектов из базы данных. При этом должна была быть возможность для групповой работы с объектами. Я добавил колонку, в grid повесил на нее CheckBox, и для всех операций сделал проверку значения данного поля. Было решено сделать PopupMenu с пунктами «Пометить все», «Снять все пометки», «Обратить пометки».

Врач вошел в палату для планового осмотра.
Он расспросил как самочувствие пациентов и уже готовился выйти.
Вдруг он разворачивается и спрашивает: «А зачем вы намотали провод от телевизора на ножку стула?»
Пациент побледнел, покраснел и сказал: «Было очень скучно…»


Делать три почти одинаковые процедуры или использовать if then else if было ниже звания программиста.

Начало

Есть логические операции, при выполнении которых результирующее значение равно первому операнду:

1 and 1 = 1
0 and 1 = 0 
1 or 0 = 1
0 or 0 = 0
1 xor 0 = 1
0 xor 0 = 0

Поэтому (при условии, что a принадлежит [0,1]):

((a or 0) and 1) xor 0 = a

Рассуждения

Стоит изменить один из операндов, система выйдет из равновесия:

((a or 0) and 0) xor 0 = 0
((a or 0) and 1) xor 1 = !a
((a or 1) and 1) xor 0 = 1

Но если пойти дальше, то можно заметить, что второй операнд есть xor от первого и третьего:

((a or 0) and (0 xor 0)) xor 0 = 0
((a or 0) and (0 xor 1)) xor 1 = !a
((a or 1) and (1 xor 0)) xor 0 = 1

Или

c = ((a or b1) and (b1 xor b2)) xor b2
b1 = 0; b2 = 0; => c = 0
b1 = 0; b2 = 1; => c = !a
b1 = 1; b2 = 0; => c = 1

Если b1 и b2 представить как первый и второй бит байта, то можно записать:

b1 = (b shr 1) and 1; b2 = b and 1;

Тогда значение b можно хранить в атрибуте tag пункта меню:

b = 0 => c =0
b = 1 => c =!a
b = 2 => c =1

Результат

function TForm1.logmath(a, b: Integer): Integer;
var
  b1, b2: Integer;
begin
   b1 := (b shr 1) and 1;
   b2 := b and 1;
   Result := (((a or b1) and (b1 xor b2)) xor b2);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   // xor
   Memo1.Lines.Add(IntToStr(0) + ' xor => ' + IntToStr(logmath(0, 1)));   // 1
   Memo1.Lines.Add(IntToStr(1) + ' xor => ' + IntToStr(logmath(1, 1)));   // 0
   // and 0
   Memo1.Lines.Add(IntToStr(0) + ' set 0 => ' + IntToStr(logmath(0, 0))); // 0
   Memo1.Lines.Add(IntToStr(1) + ' set 0 => ' + IntToStr(logmath(1, 0))); // 0
   // or 1
   Memo1.Lines.Add(IntToStr(0) + ' set 1 => ' + IntToStr(logmath(0, 2))); // 1
   Memo1.Lines.Add(IntToStr(1) + ' set 1 => ' + IntToStr(logmath(1, 2))); // 1
end;

С практической точки зрения

procedure TForm1.Button2Click(Sender: TObject);
var
  i: Integer;

  function logmath(a: boolean; b: Integer): boolean;
  var
    b1, b2: Boolean;
  begin
     b1 := ((b shr 1) and 1) = 1;
     b2 := (b and 1) = 1;
     Result := (((a or b1) and (b1 xor b2)) xor b2);
  end;
begin
   // TButton(Sender).Tag: 2 - включить
   //                      0 - выключить
   //                      1 - переключить
   for i := 0 to ComponentCount-1 do
      if Components[i] is TCheckBox then
         (Components[i] as TCheckBox).Checked :=
            logmath((Components[i] as TCheckBox).Checked, TButton(Sender).Tag);
end;

Автор: Thunderchild.

.: Пример к данной заметке :.


При использовании материала - ссылка на сайт обязательна