60 функций PicoLisp. Часть 4: Управляющие конструкции
Перевод. Оригинал здесь
Это седьмая часть цикла Введение в PicoLisp и четвертая из серии "60 функций PicoLisp, которые вам пригодятся". В ней речь пойдёт о циклах и условных операторах.
Итак, следующая тема, которая будет рассмотрена в цикле "Введение в
PicoLisp", это условные операторы. Давайте начнём с функци if
. Она
чрезвычайно проста:
: (if (> 4 3) (println 'OK) (println 'Bad))
OK
-> OK
Первым в функцию передаётся условие, если оно истинно, то выполняется второй переданный аргумент, если ложно, то третий.
На самом деле, если условие ложно, то выполняются все переданные аргументы, начиная с третьего (прим. перев.)
Сложные условия можно составлять с помощью функций and
и or
:
: (if (and (> 4 2)(> 4 3)) (println 'OK) (println 'Bad))
OK
-> OK
: (if (or (> 4 2)(> 4 5)) (println 'OK) (println 'Bad))
OK
-> OK
Также для составления условий вы можете использовать функции not
,
xor
nor
и nand
. Подробнее о том, что означают эти страшные слова,
можно узнать из основ булевой алгебры.
Также в PicoLisp есть специальная функция ifn
, которая сочетает if
и not
: если
условие ложно, выполняется первое выражение, если истинно - то второе:
: (ifn (> 4 3) (println 'OK) (println 'Bad))
Bad
-> Bad
Кроме if
есть ещё несколько функций передачи потока управления. Их
использование позволяет сделать код более понятым и красивым. Их конечно
же можно заменить на вложенные вызовы if
в сочетании с булевой
алгеброй. Подробнее о них можно прочесть в справочном
руководстве:
ifn
, о которой уже сказано, и которая исполняется, если переданное условие вычисляется в NIL.when
, которая очень похожа наif
, за исключением того, что позволяет вычислить несколько выраженией, если условие истинно. Противоположностью функцииwhen
являетсяunless
.cond
- принимает на вход несколько списков, в каждом из которых первый элемент является условием, а остальные вычисляются, если условие истинно. Если все условия ложны, функция возвращаетNIL
. Противоположностьюcond
являетсяnond
.
Давайте посмотрим, как работают эти функции, передавая в качестве
условия T
или NIL
, а в качестве вычисляемых выражений - три простых
вызова функции prinl
- (prinl 1) (prinl 2) (prinl 3)
.
if
и ifn
:
# Условие истинно, выполняется первое выражение
: (if T (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Первое
-> "Первое"
# Условие ложно, выполняется второе и последующие выражения
: (if NIL (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Второе
Третье
-> "Третье"
# ifn действует наоборот
: (ifn T (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Второе
Третье
-> "Третье"
: (ifn NIL (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Первое
-> "Первое"
when
и unless
:
# Условие истинно, выполняются все три выражения
: (when T (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Первое
Второе
Третье
-> "Третье"
# Условие ложно, ничто не выполняется, возвращается NIL
: (when NIL (prinl "Первое") (prinl "Второе") (prinl "Третье"))
-> NIL
# unless действует наоборот
: (unless T (prinl "Первое") (prinl "Второе") (prinl "Третье"))
-> NIL
: (unless NIL (prinl "Первое") (prinl "Второе") (prinl "Третье"))
Первое
Второе
Третье
-> "Третье"
cond
и nond
:
# истина только в третьей паре, первые две не исполняются
: (cond (NIL (prinl "первое")) (NIL (prinl "второе")) (T (prinl "третье")))
третье
-> "третье"
# истинных условий нет, ни одно выражение не исполняется
: (cond (NIL (prinl "первое")) (NIL (prinl "второе")) (NIL (prinl "третье")))
-> NIL
# ложь в первом же условии, поэтому выполняется только первое выражение
: (nond (NIL (prinl "первое")) (NIL (prinl "второе")) (T (prinl "третье")))
первое
-> "первое"
# ложь в третьем условии, первые два выражения игнорируются
: (nond (T (prinl "первое")) (T (prinl "второе")) (NIL (prinl "третье")))
третье
-> "третье"
Давайте начнём с цикла for
. В простейшей форме он принимает символ,
число и выражение. Он выполняет выражение, подставляя вместо символа
текущее число, начиная с единицы:
: (for N 5 (printsp N))
1 2 3 4 5 -> 5
Можно подать вместо числа список. Тогда цикл for
выполняет выражение,
подставляя в него элементы списка по очереди:
# num? определяет, является ли аргумент числом
: (for N (1 "строка" 2 "опять строка") (printsp (num? N)))
1 NIL 2 NIL -> NIL
Цикл while
выполняется до тех пор, пока условие не принимает значение
NIL:
: (while (read) (prinl "Вы ввели: " @))
abc
Вы ввели: abc
1 2 3
Вы ввели: 1
Вы ввели: 2
Вы ввели: 3
NIL
-> 3
Символ @
очень полезен, так как возвращает значение последнего
выполненного выражения. У него также есть несколько других функций, о
которых можно прочитать здесь
Цикл do
выполняет выражение, переданное вторым аргументом столько раз,
сколько вы ему прикажете числом, переданным в качестве первого
аргумента:
: (do 4 (printsp 'OK))
OK OK OK OK -> OK
Третьим аргументом вы можете передать условие для досрочного выхода из цикла:
: (do 2 (printsp 'OK) (T (= 3 3) (printsp 'done)))
OK done -> done
Условие (T (= 3 3) (printsp 'done))
соответствует выражению:
(if (= 3 3) (printsp 'done))
. Понятно, что в данном случае оно всегда
истинно и выход из цикла происходит в конце первой же итерации. Вот ещё
пример:
: (do 2 (printsp 'OK) (T (< 3 3) (printsp 'done)))
OK OK -> NIL
В этом примере условие всегда ложно, поэтому досрочный выход из цикла не происходит.
loop
- бесконечный цикл. Посмотрите на следующий пример:
- Переменная N устанавливается равной трём, затем стартует цикл
- Печатается значение N
- Проверяется, "верно ли, что (N - 1) равно нулю?"
- Если условие оказывается верным, происходит выход из цикла.
: (let N 3
(loop
(prinl N)
(T (=0 (dec 'N)) 'done) ) )
3
2
1
-> done
В последних двух статьях мы рассмотрели наиболее употребительные функции ввода/вывода, ветвления и циклов. Во многих примерах не показаны все опции, которые можно передавать в функцию, но вы всегда можете ознакомиться с ними в официальной документации.