picolisp.ru
Переключить темный/светлый/авто режим Переключить темный/светлый/авто режим Переключить темный/светлый/авто режим Back to homepage

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

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