6.4 Логические операторы

Common Lisp содержит три логических оператора для булевых значений: and, or и not (и, или, не, соответственно). and и or являются управляющими структурами, потому что их аргументы вычисляются в зависимости от условия. Функции not необходимо инвертировать её один аргумент, поэтому она может быть простой функцией.

[Функция] not x

not возвращает t, если x является nil, иначе возвращает nil. Таким образом она инвертирует аргумент как булево значение.

null то же, что и not, обе функции включены для ясности. По соглашению принято использовать null, когда надо проверить пустой ли список, и not, когда надо инвертировать булево значение.


[Макрос] and {form}*

(and form1 form2 ... ) последовательно слева направо вычисляет формы. Если какая-либо форма formN вычислилась в nil, тогда немедленно возвращается значение nil без выполнения оставшихся форм. Если все формы кроме последней вычисляются в не-nil значение, and возвращает то, что вернула последняя форма. Таким образом, and может использоваться, как для логических операций, где nil обозначает ложь и не-nil значения истину, так и для условных выражений. Например:

(if (and (>= n 0)
         (< n (length a-simple-vector))
         (eq (elt a-simple-vector n) ’foo))
    (princ "Foo!"))

Выражение выше выводит Foo!, если n-ый элемент вектора a-simple-vector является символом foo, проверяя при этом вхождения n в границы вектора a-simple-vector. elt не будет вызвано с аргументом n выходящим за границы вектора, так как and гарантирует ленивую проверку аргументов слева направо.

Оператор and отличается тем, что в определённых случаях вычисляет не все аргументы.

Запись предыдущего примера

(and (>= n 0)
     (< n (length a-simple-vector))
     (eq (elt a-simple-vector n) ’foo)
     (princ "Foo!"))

будет выполнять ту же функцию. Разница в них только стилистическая. Некоторые программисты никогда не используют в форме and выражения с побочными эффектами, предпочитая для этих целей использовать if или when.

Из общего определения можно сделать дедуктивный вывод о том, что (and x)  x. Также (and) выполняется в t, который тождественен этой операции.

Можно определить and в терминах cond таким образом:

(and x y z ... w)  (cond ((not x) nil)
((not y) nil)
((not z) nil)

(t w))

Смотрите id и when, которые иногда являются стилистически более удобными, чем and в целях ветвления. Если необходимо проверить истинность предиката для всех элементов списка или вектора (element 0 and element 1 and element 2 and ), можно использовать функцию every.


[Макрос] or {form}*

(or form1 form2 ... ) последовательно выполняет каждую форму слева направо. Если какая-либо непоследняя форма выполняется в что-либо отличное от nil, or немедленно возвращает это не-nil значение без выполнения оставшихся форм. Если все формы кроме последней, вычисляются в nil, or возвращает то, что вернула последняя форма. Таким образом or может быть использована как для логических операций, в который nil обозначает ложь, и не-nil истину, так и для условного выполнения форм.

Оператор or отличается тем, что в определённых случаях вычисляет не все аргументы.

Из общего определения, можно сделать дедуктивный вывод о том, что (or x)  x. Также, (or) выполняется в nil, который тождественен этой операции.

Можно определить or в терминах cond таким образом:

(or x y z ... w)  (cond (x) (y) (z) ... (t w))

Смотрите id и unless, которые иногда являются стилистически более удобными, чем or в целях ветвления. Если необходимо проверить истинность предиката для всех элементов списка или вектора (element 0 or element 1 or element 2 or ), можно использовать функцию some.