Программирование на Lazarus

Логические типы, конструкции и компоненты

Показывать лекцию целиком

Цель лекции

Изучение способов работы с логическими типами данных. Изучение основных компонентов для работы с этими типами.

Логический тип данных

В предыдущих лекциях мы уже встречались с таким типом - некоторые свойства компонентов могли принимать только одно из двух значений: либо True (Истина), либо False (Ложь). Это и есть логический тип данных. В Паскале это тип Boolean, который занимает один байт оперативной памяти. В Lazarus, кроме того, есть типы ByteBool (тоже 1 байт), WordBool (2 байта) и LongBool (4 байта). Все эти типы могут принимать лишь одно из двух значений: true или false. На практике, правда, такое многообразие не применяется, программисты вполне обходятся только одним стандартным типом boolean. Вы можете создать переменную такого типа и присвоить ей true или false. Кроме того, нередко встречаются выражения, которые в результате тоже дают либо Истину, либо Ложь, они также считаются логическими. Например, если вы присвоите логической переменной выражение 3>2, то в переменную попадет True, так как три действительно, больше двух.

Итак, что же можно делать с логическими данными, помимо присваивания им значений True или False? Их можно сравнивать между собой, используя для этого операторы сравнения:

= (Равно)
<> (Не равно)
> (Больше) - Здесь следует иметь в виду, что компилятор, на самом деле, вместо False записывает двоичное значение 0, а вместо True - 1. Таким образом, True всегда будет больше, чем False.
< (Меньше)
>= (Больше или равно)
<= (Меньше или равно)

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

  • NOT (Логическое НЕ)
  • AND (Логическое И)
  • OR (Логическое ИЛИ)
  • XOR (Логическое исключающее ИЛИ).
  • Эти операторы сравнивают между собой два логических значения (операнда) и в зависимости от этих значений возвращают в качестве результата true или false. Следующая таблица демонстрирует результат этих операций (сравниваются логические переменные a и b):

    Логические операции
    A B not A A and B A or B A xor B
    true true false true true false
    false true true false true true
    true false false false true true
    false false true false false false

    Давайте разберем действия этих операций подробней.

    Операция NOT (НЕ), в отличие от других логических операций, работает только с одним операндом. Это - операция-перевертыш: если операнд содержит True, то возвращаемый результат будет False, и наоборот. Эту операцию очень часто применяют для того, чтобы поменять значение логической переменной (или свойства) на противоположное, например,

    A:= not A;
        

    Операция AND (И) возвращает True только в том случае, если оба сравниваемых операнда содержат True. Если первый операнд содержит False, дальнейшая проверка уже не проводится (смысла нет), и AND возвращает False. Если первый операнд содержит True, то AND делает проверку второго операнда - если он также содержит True, то и возвращаемый результат будет True, иначе возвращается False.

    Операция OR (ИЛИ) действует похожим образом, но возвращает True в том случае, если хоть один из операндов содержит True. OR проверяет первый операнд. Если он True, дальнейшая проверка уже не производится, и OR возвращает True. Иначе OR проверяет второй операнд - если он True, то и возвращается True, в противном случае возвращается False.

    Операция XOR (Исключающее ИЛИ) всегда проверяет оба операнда, и возвращает True только тогда, когда один из операндов True, а другой обязательно False. Правда, на практике XOR обычно не используют, поскольку всегда проще выполнить проверку A <> B, которая возвращает такой же точно результат.

    Итак, примеры работы с логическими данными (в Lazarus их выполнять не нужно):

    var
       A, B, C: Boolean;
    begin
       A:= True;   //результат - True
       B:= not A;   //результат - False
       C:= A and B;  //результат - False
       C:= A or B;   //результат - True
       C:= A <> B;   //результат - True
       C:= 3 > 5;   //результат - False
       …
        

    Управляющая конструкция IF

    От логического типа данных было бы мало пользы, если бы мы не могли применять различные действия в зависимости от результатов проверки - если в переменной находится Истина, тогда выполнить один код, иначе выполнить другой код. Именно такую возможность предоставляет управляющая конструкция IF.

    Простейший синтаксис этой конструкции такой:

    if Условие then Действие;
        

    Английское IF переводится как ЕСЛИ, а THEN - ТОГДА. Тут все довольно понятно: если какое-то условное выражение (логическая переменная или логическое свойство компонента) имеет значение True, тогда выполняется указанное действие. Теперь мы можем изучать примеры на практике. Загрузите Lazarus с новым проектом. Посреди окна установите кнопку TButton. Сохраните проект в папку 07-01 под именем MyBool; не забудьте главную форму назвать fMain, а сохраняемый модуль - Main. Затем сгенерируйте событие нажатия на кнопку, которое оформите следующим образом:

    procedure TfMain.Button1Click(Sender: TObject);
    var
      b: boolean;
    begin
      b:= True;
      if b then ShowMessage('Истина');
    end;
        

    Здесь все предельно просто: объявляем логическую переменную b, затем присваиваем ей значение True (Истина). В строке

      if b then ShowMessage('Истина');
        

    мы проверяем: если b имеет значение True (то есть, если b истинна), тогда мы выводим сообщение "Истина". Сохраните и запустите проект. Нажатие на кнопку вызовет вывод сообщения. Можете для примера присвоить b значение False - в этом случае нажатие на кнопку не вызовет никакого сообщения.

    Кстати, в данном случае в качестве условия мы указали просто имя переменной b. Ведь она уже содержит значение True! Так что строка

      if b then ShowMessage('Истина');
        

    будет полностью аналогична строке с полным условием

      if b = True then ShowMessage('Истина');
        

    Если мы будем расширять условие с помощью условных операций, то все операнды следует заключить в скобки. Исключение составляет операция NOT, которая работает только с одним операндом. Изменим код:

    procedure TfMain.Button1Click(Sender: TObject);
    var
      a,b: boolean;
    begin
      a:= False;
      b:= True;
      if (a = b) or b then ShowMessage('Истина');
    end;
        

    Здесь мы проверяем уже два условия. И если первое (a = b) вернет нам False, то второе b вернет True. И так как мы используем операцию OR, нам достаточно, чтобы хоть один из операндов был True. Значит, сообщение будет выполнено. Если бы во втором операнде мы сравнивали b еще с чем-то, то его тоже следовало бы поместить в скобки. Например,

      if (a = b) or (b > a) then ShowMessage('Истина');
        

    В этом примере сообщение "Истина" будет выведено, так как во втором операнде b действительно больше, чем a. Если вы забудете про скобки, компилятор не станет указывать вам на ошибку, и программа будет запущена. Однако результат может оказаться неверным. Например, если мы уберем скобки в первом примере:

      if a = b or b then ShowMessage('Истина');
        

    То сообщение "Истина" не будет выполнено. Ведь оператор IF делает только одну проверку, значит, он проверит только a = b. Часть условия с OR будет пропущена, потому что мы не заключили операнды в скобки. Так что если вы в условии применяете операции AND, OR или XOR, не забывайте заключать операнды в скобки. Скобки можно не использовать, если в качестве операндов вы указываете по одной условной переменной или свойству, например, код:

      if a  or b then ShowMessage('Истина');
        

    выведет сообщение "Истина", так как в b содержится True.

    Управляющий оператор IF не зря называют конструкцией, его синтаксис может состоять из нескольких частей:

    if Условие then Действие1
    else Действие2;
        

    Английское ELSE переводится как ИНАЧЕ. Здесь, ЕСЛИ какое-то условие вернет True, то будет выполнено Действие1, ИНАЧЕ будет выполнено Действие2. Обратите внимание, что перед ELSE точку с запятой НЕ СТАВЯТ! Изменим код:

    procedure TfMain.Button1Click(Sender: TObject);
    var
      b: boolean;
    begin
      b:= True;
      if b then ShowMessage('Истина')
      else ShowMessage('Ложь');
    end;
        

    Нажатие на кнопку вызовет сообщение "Истина", однако если затем вы исправите код, и в переменную b вместо True поместите False, то выйдет сообщение "Ложь" - проверьте это сами.

    Однако конструкция IF может быть и сложней. Что, если нам нужно проверить не одно, а два условия? Или больше? Полный синтаксис этой конструкции такой:

    if Условие1 then Действие1
    else if Условие2 then Действие2
    …
    else if УсловиеN then ДействиеN
    else ДействиеИНАЧЕ;
        

    Давайте разбираться. В этой конструкции, если Условие1 вернет True, то будет выполнено Дествие1, и на этом конструкция закончится. Если же оно вернет False, проверка будет продолжена. Если следующее Условие2 вернет True, то будет выполнено Дествие2, и так далее, подобных проверок может быть сколько угодно. Если же все проверки условий вернут False, то будет выполнено ДействиеИНАЧЕ. Изменим код:

    procedure TfMain.Button1Click(Sender: TObject);
    var
      a,b: boolean;
    begin
      a:= False;
      b:= True;
      if a then ShowMessage('Действие №1')
      else if b then ShowMessage('Действие №2')
      else if a or b then ShowMessage('Действие №3')
      else ShowMessage('Действие №4');
    end;
        

    Как вы думаете, какое сообщение выйдет в данном случае? И сколько сообщений выйдет? Если вы сказали, что выйдет только одно сообщение "Действие №2", то вы усвоили конструкцию IF. Иначе советую вернуться назад, и внимательней изучить всё вышесказанное.

    Операторские скобки BEGIN…END

    Логический оператор IF, как и многие другие операторы, которые нам еще предстоит изучить, выполняет только одно действие. Взгляните на следующий код:

    if a then
       ShowMessage('Действие №1');
       ShowMessage('Действие №2');
       ShowMessage('Действие №3');
        

    В этом примере сообщение "Действие №1" выйдет только в том случае, если a = True. Остальные сообщения выйдут в любом случае, так как они к оператору IF уже не относятся, а выполняются как самостоятельные операторы. Однако что делать, если в рамках конструкции IF нам нужно выполнить не один оператор, а целый блок кода? В таком случае, этот блок помещают в операторские скобки begin…end, в результате чего блок операторов выполняется, как единый оператор. Изменим код:

    procedure TfMain.Button1Click(Sender: TObject);
    var
      a: boolean;
    begin
      if a then begin
        ShowMessage('Действие №1');
        ShowMessage('Действие №2');
        ShowMessage('Действие №3');
      end; //конец if
    end;
        

    В данном примере все три сообщения заключены в скобки begin…end и выполняются, как один оператор. Значит, они все будут выведены, только если a = True, иначе не будет выведено ни одно сообщение.

    Здесь следует сказать пару слов о стиле программирования. Единых стандартов тут нет, есть только рекомендации.

    Во-первых, код должен быть удобочитаем. Сравните:

      if a then ShowMessage('Действие №1') else ShowMessage('Действие №2');
        

    Синтаксис здесь правильный, компилятор ошибок не найдет. И код этот будет выполнен верно, однако как же его сложно читать! Все части конструкции IF сливаются в одну строку, сразу и не разберешь, что тут к чему, а ведь конструкция еще очень простая! Что, если бы мы применяли несколько операций AND или OR, да еще с множеством операндов? Совсем иначе смотрится такой код:

      if a then
      ShowMessage('Действие №1')
      else
      ShowMessage('Действие №2');
        

    Здесь конструкция разбита на несколько небольших логических частей, каждой части отведена отдельная строка. Код стал явно более понятным, как говорят, более удобочитаемым. Однако его можно еще улучшить, если сдвинуть вложенные операторы вправо на 2-3 пробела, чтобы показать зависимости:

      if a then
         ShowMessage('Действие №1')
      else
        ShowMessage('Действие №2');
        

    Хороший программист именно так и оформляет свой код. Тут сразу видно, что от чего зависит, конструкция становится понятней.

    Во-вторых, если вы применяете операторские скобки, то в учебной литературе вы можете встретить два способа оформления кода. Такой:

      if a then begin
        ShowMessage('Действие №1');
        ShowMessage('Действие №2');
        ShowMessage('Действие №3');
      end; //конец if
        

    и такой:

      if a then
      begin
        ShowMessage('Действие №1');
        ShowMessage('Действие №2');
        ShowMessage('Действие №3');
      end; //конец if
        

    Оба этих способа считаются классическими, оба хорошо смотрятся, какой из них применять - ваш выбор. Лично я не люблю переносить BEGIN на отдельную строку (разве что строка получается слишком длинной), поэтому предпочитаю первый способ. Вы, если захотите, можете выбрать и второй вариант, он тоже правильный.

    Флажки и радиокнопки

    В программах, особенно в настройках, нередко используют флажки (которые иначе называют галочками) и радиокнопки. Флажки TCheckBox имеют логическую зависимость включен/выключен, поэтому изучим их в этой лекции. Радиокнопки TRadioButton также имеют такую зависимость. И флажок, и радиокнопка находятся на вкладке Standard Палитры компонентов:

    (рис 7.1) TCheckBox и TRadioButton

    Установите на форму один флажок. Посмотрим на его свойства.

    Name задает имя компонента. Caption - пояснительный текст справа от флажка. Самое главное свойство для нас - Checked (англ. checked - проверенный). Оно имеет логический тип. Если в этом свойстве значение True, то флажок включен, иначе он выключен. Значение меняется автоматически, хотя его можно изменить и программно.

    Свойство Name изменим на ChB1, чтобы проще к нему обращаться. В свойстве Caption укажите текст

    Наш флажок
        

    Вы можете заметить, что по мере ввода текста в свойство Caption ширина компонента увеличивается, чтобы уместился весь текст. Теперь изменим код обработки нажатия на кнопку:

    procedure TfMain.Button1Click(Sender: TObject);
    begin
      if ChB1.Checked then
        ShowMessage('Наш флажок включен!')
      else
        ShowMessage('Наш флажок выключен!');
    end;
        

    Вы и без комментариев должны понять, что если в свойстве Checked нашего флажка ChB1 содержится значение True, то выйдет одно сообщение, иначе - другое. Сохраните проект и запустите его на выполнение. Включайте или выключайте флажок, после чего нажимайте кнопку. Сообщение скажет, включен флажок, или выключен.

    State (состояние) - еще одно полезное свойство флажка. Это свойство определяет первоначальное значение флажка, кроме того, можно проверить его и в процессе работы программы. Может иметь следующие значения:

  • cbChecked - включен
  • cbGrayed - неопределен (от англ. gray - серый)
  • cbUnchecked - выключен
  • Еще нас могут интересовать два события, которые доступны на вкладке События Инспектора объектов.

    OnChange - При изменении. Событие возникает каждый раз, когда пользователь включает или выключает флажок. Вы можете сгенерировать это событие так же, как и событие нажатия на кнопку - дважды щелкнув по нему в Инспекторе объектов. Затем можете скопировать код из события кнопки, а событие кнопки очистить:

    (рис 7.2) Событие OnChange флажка

    Теперь сообщения будут выходить сразу же при изменении состояния флажка. Кнопка же никаких действий совершать не будет. Однако не спешите ее удалять, она еще понадобится.

    OnClick - событие также возникает, когда пользователь щелкнет мышью по компоненту, включив флажок, или выключив его.

    Разница между этими событиями в том, что OnChange будет выполнен в любом случае - если пользователь мышью включил/выключил флажок, или если мы сделали это программно, например, вписав в событие кнопки Button1Click (между begin и end) код:

    ChB1.Checked:= not ChB1.Checked;
        

    Тогда при нажатии на кнопку состояние флажка изменится на противоположное - сработал наш перевертыш NOT. OnChange при этом сработает, а OnClick - нет, ведь мышью по компоненту не щелкали!

    Теперь займемся радиокнопками. Если флажок можно использовать в единичном варианте, то радиокнопок должно быть как минимум две. Радиокнопки позволяют пользователю выбрать один из возможных вариантов, но для выбора одной такой кнопки будет явно недостаточно.

    Давайте сымитируем выборы президента, установив 5 таких радиокнопок одну под другой. Свойства Name этих кнопок переименуем, соответственно, в RB1, RB2, RB3, RB4 и RB5. Ну, а в свойствах Caption этих кнопок запишем, соответственно

    Путин
    Медведев
    Жириновский
    Зюганов
    Прохоров
        

    Теперь нам нужно включить одну из этих радиокнопок. Поскольку нынешний президент у нас Путин, переведем в True свойство Checked радиокнопки RB1. В результате у нас получится такая форма:

    (рис 7.3) Флажок и радиокнопки

    Теперь изменим код нажатия на кнопку:

    procedure TfMain.Button1Click(Sender: TObject);
    begin
      if RB1.Checked then
        ShowMessage('Вы выбрали Путина')
      else if RB2.Checked then
        ShowMessage('Вы выбрали Медведева')
      else if RB3.Checked then
        ShowMessage('Вы выбрали Жириновского')
      else if RB4.Checked then
        ShowMessage('Вы выбрали Зюганова')
      else
        ShowMessage('Вы выбрали Прохорова');
    end;
        

    Тут все достаточно прозрачно, обработка радиокнопок практически не отличается от обработки флажков. Единственное различие: пользователь не сможет отметить несколько радиокнопок, только одну из них. А флажки могут быть включены все, некоторые, или ни один. Раз уж мы создаем имитацию выборов, неплохо бы и в свойстве Caption формы написать Выборы. Сохраните проект, запустите на выполнение и играйте на здоровье в выборы президента.

    Контейнеры для флажков и радиокнопок

    Можно конечно, пользоваться и единичными компонентами TCheckBox и TRadioButton, однако куда проще использовать для этого контейнеры, особенно когда флажков и радиокнопок много. Для флажков используется контейнер TCheckGroup, а для радиокнопок - TRadioGroup. Оба компонента располагаются на вкладке Standard Палитры компонентов. Чтобы не мучаться с изменением дизайна и кода нашего проекта, будет проще этот проект закрыть, и создать новый.

    Свойство Name формы переименуйте сразу в fMain, в свойстве Caption впишите текст

    Выборы
        

    Свойство BorderStyle имеет смысл перевести в bsDialog, а Position - в poDesktopCenter. Далее, сохраните проект под именем election в папку 07-02, модулю главной формы дайте имя Main.

    Теперь можем заняться контейнерами:

    (рис 7.4) Контейнеры для радиокнопок и флажков

    Слева на форму установите контейнер для радиокнопок TRadioGroup. Выделите его, и измените свойство Name на RG1. Свойство Caption контейнера формирует общий заголовок, впишем там слово Кандидаты.

    Теперь обратите внимание на свойство Items. Это сложное свойство, содержит текст всех радиокнопок (набор строк). Щелкните по кнопке "" правее свойства:

    (рис 7.5) Кнопка Диалога ввода строк

    Откроется простой текстовый редактор, где вы можете указать текст ваших радиокнопок. Текст каждой радиокнопки должен располагаться на отдельной строке. Как и в прошлом случае, введите такие строки:

    Путин
    Медведев
    Жириновский
    Зюганов
    Прохоров
        

    Обратите внимание: если вы будете увеличивать высоту контейнера, расстояние между полученными радиокнопками будет увеличиваться. И наоборот. Подберите наиболее оптимальное расстояние между ними.

    Правее этого контейнера установите контейнер для флажков TCheckGroup. Его свойство Name переименуйте в ChG1, а в свойстве Caption напишите Города. Этот контейнер также имеет сложное свойство Items, которое содержит текст всех флажков. Щелкните по кнопке "" правее этого свойства, и в Диалоге ввода строк запишите такие строки:

    Москва
    Санкт-Петербург
    Воронеж
    Саратов
    Уфа
        

    В нашем проекте это будут проголосовавшие города. Ниже контейнеров установите кнопку, в свойстве Caption которой напишите Результат. Окончательный вид окна у вас должен быть примерно таким:

    (рис 7.6) Окно программы election

    Теперь, прежде чем мы приступим к программированию события нажатия на кнопку, разберем еще кое-что. Контейнер флажков TCheckGroup имеет свойство Checked, которое будет доступно при работе программы. Это свойство логического типа, оно указывает, включен ли флажок. А чтобы разобраться, какой именно флажок включен, после свойства, в квадратных скобках, указывают индекс флажка. Индексы начинаются с нуля, поэтому, чтобы узнать, включен ли первый флажок, нужно выполнить код

    if ChG1.Checked[0] then …
        

    Не забывайте - индексы флажков всегда начинаются с нуля!

    Контейнер радиокнопок TRadioGroup имеет свойство ItemIndex. Это свойство указывает, какую именно радиокнопку пользователь выбрал, индексация у них также начинается с нуля. По умолчанию, свойство равно -1. Это означает, что никакая радиокнопка не выбрана. Таким образом, чтобы выполнить действие для первой радиокнопки, мы должны проверить, выбрана ли она:

      if RG1.ItemIndex = 0 then …
        

    Чтобы повторить предыдущий пример, укажем 0 в свойстве ItemIndex (здесь индексы тоже начинаются с нуля) - радиокнопка Путин окажется выбранной по умолчанию. Не забывайте после ввода в свойства каких-то значений нажимать <Enter>, чтобы подтвердить изменения.

    Теперь, когда мы знаем, как определить, какие флажки включены и какая радиокнопка выбрана, займемся программированием события нажатия на кнопку.

    procedure TfMain.Button1Click(Sender: TObject);
    var
      s: String; //для формирования строки с проголосовавшими городами
    begin
      //сначала укажем победителя:
      if RG1.ItemIndex = 0 then
        ShowMessage('Вы выбрали Путина')
      else if RG1.ItemIndex = 1 then
        ShowMessage('Вы выбрали Медведева')
      else if RG1.ItemIndex = 2 then
        ShowMessage('Вы выбрали Жириновского')
      else if RG1.ItemIndex = 3 then
        ShowMessage('Вы выбрали Зюганова')
      else
        ShowMessage('Вы выбрали Прохорова');
    
      //теперь нужно что-то присвоить переменной s, чтобы инициализировать
      //ее. присвоим просто пустую строку:
      s:= '';
      //теперь будем собирать строку s, в зависимости от включенных флажков
      //после каждой строки будем вставлять знак перехода на новую строку
      if ChG1.Checked[0] then s:= s + 'Москва проголосовала' + #13;
      if ChG1.Checked[1] then s:= s + 'Санкт-Петербург проголосовал' + #13;
      if ChG1.Checked[2] then s:= s + 'Воронеж проголосовал' + #13;
      if ChG1.Checked[3] then s:= s + 'Саратов проголосовал' + #13;
      if ChG1.Checked[4] then s:= s + 'Уфа проголосовала' + #13;
    
      //Если хоть один флажок был включен, то строка s не пуста.
      //покажем ее в этом случае:
      if s <> '' then ShowMessage(s);
    end;
        

    Комментарии исчерпывающие, каких то затруднений с кодом у вас возникнуть не должно. Единственное, замечу, что если не проинициализировать вначале переменную s, то есть, не присвоить ей какое-то значение, хоть и пустую строку, в окне сообщений при компиляции может выйти напоминание:

    Warning: Local variable "s" does not seem to be initialized
        

    Дело в том, что дальше мы пытаемся использовать значение строки s:

    … s:= s + 'Москва проголосовала' + #13;
        

    А компилятор не знает, имеется ли там что-то, или просто мусор? Ошибки не будет, но все равно, переменные желательно вначале инициализировать: строковым присваивать пустую строку, числовым - значение 0 (обнулять). Логические по умолчанию сами принимают значение False.

    Сохраните проект и запустите его на выполнение.

    Вернуться к учебному плану