На этом шаге мы рассмотрим возможность создания составных операторов.
Функция PROG1 превращает стандартную функциональную запись LISP-программы в нефункциональную в виде последовательности обращений к функциям, напоминающую запись программы на языке императивного программирования.
Синтаксис функции PROG1:
(PROG1 S-выражение1 S-выражение2 ... S-выражениеN)
Функция имеет переменное число аргументов, которые она последовательно вычисляет и возвращает в качестве значения значение первого. Например:
$ (PROG1 (SETQ X 2) (SETQ Y (+ X 2))) 2 $ Y 4
Функцию PROG1 часто используют для того, чтобы не вводить временных переменных для хранения результатов в процессе вычисления других выражений.
Замечание. В версии muLISP85 есть следующие функции, облегчающие поддержку стиля
императивного программирования: PROG1, PROGN, CATCH, THROW, UNWIND-PROTECT.
1. Функция (PROGN FORM1 FORM2 ... FORMN) последовательно вычисляет формы, начиная с FORM1, и возвращает значение последней вычисленной формы.
Как будет вычисляться форма, зависит от структуры списка форм, т.е.:
Если значение предиката (т.е. (EVAL (CAR FORM))) есть NIL, то значение формы FORM - также NIL.
Однако если значение предиката - не NIL, то все остальные формы из PROGN удаляются, а вместо этого последовательно вычисляются формы, стоящие в теле неявного COND.
Если тело COND пустое, то функция PROGN возвращает значение предиката;
(PROGN (SETQ NUM1 (+ 2 5)) (SETQ NUM2 (* 3 4)) ( (< NUM1 NUM2) ( (MINUSP NUM1) (* 3 NUM2) ) (+ NUM1 NUM2) ) )
2. Функция (PROG1 FORM1 ... FORMN) вычисляет FORM1, затем - оставшиеся формы, используя неявную функцию PROGN, и возвращает результат вычисления FORM1. Например:
$ (SETQ FOO '(A B C D)) (A B C D) $ (PROG1 (CAR FOO) (SETQ FOO (CDR FOO))) A $ FOO (B C D)
3. Функция (CATCH LABEL FORM1 ... FORMN) возвращает результат вычисления форм с использованием неявной функции PROGN, если в процессе вычисления не произойдет переход на метку LABEL. Если такой переход произошел, то функция CATCH возвращает значение, установленное при переходе. Аргумент LABEL вычисляется до вычисления форм.
Если LABEL есть NIL, то все переходы, происходящие в процессе вычисления форм, "улавливаются" функцией CATCH.
4. Функция (THROW LABEL OBJECT) непосредственно заканчивает выполнение текущего тела функции, присваивает переменной THROW значение OBJECT и передает управление обратно на предыдущую функцию CATCH, имеющую метку LABEL или метку NIL.
Функция CATCH возвращает OBJECT (значение переменной THROW). Если функция CATCH не найдена, то управление возвращается к выполнимому драйверу muLISP.
Отметим, что функция THROW - это вычисляемая функция, поэтому LABEL и OBJECT вычисляются до того, как возникает переход. Например:
$ (CATCH 'FOO (PRINT 'DOG) (CATCH 'BAR (PRINT 'CAT) (THROW 'FOO 'COW) (PRINT 'PIG) ) (PRINT 'RAT) ) DOG CAT COW
5. Функция (UNWIND-PROTECT FORM1 ... FORMN) вычисляет FORM1, а затем - оставшиеся формы с помощью неявной функции PROGN, даже если вычисление FORM1 закончилось переходом.
Когда выполнение функции PROGN заканчивается, функция UNWIND-PROTECT возвращает результат вычисления FORM1, если оно закончилось нормально. В противном случае функция продолжает выполнять переход, если вычисление FORM1 закончилось переходом.
Функция UNWIND-PROTECT удобна для использования в тех случаях, когда нужно "привести в порядок" что-либо после вычисления FORM1 независимо от того, как завершилось это вычисление.
Например, данная функция будет гарантией того, что после вычисления числа "пи" с высокой точностью для точности будет восстановлено ее первоначальное значение, даже если вычисление "пи" было прервано с клавиатуры или возникла ошибка "Memory full":
(PROGN (SETQ OLD-PRECISION (PRECISION)) (UNWIND-PROTECT (PROGN (PRECISION 100) (COMPUTE-PI)) (PRECISION OLD-PRECISION)) )
На следующем шаге мы рассмотрим организацию циклических конструкций.