ТОП 10:

Программа состоит из форм и функций



Под формой (form) понимается такое символьное выражение, значение которого может быть найдено интерпретатором. Ранее мы уже использовали наиболее простые формы языка: константы, переменные, лямбда-вызовы, вызовы функций и их сочетание. Кроме них были рассмотрены некоторые специальные формы, такие как QUOTE и SETQ, трактующие свои аргументы иначе, чем обычные функции. Лямбда-выражение без фактических параметров не является формой.

Вычислимые выражения можно разделить на три группы:

1) Самоопределенные (self-evaluating) формы. Эти формы, подобно константам, являются лисповскими объектами, представляющими лишь самих себя. Это такие формы, как числа и специальные константы Т и NIL, а также знаки, строки и битовые векторы, которые мы рассмотрим далее. Ключи, начинающиеся с двоеточия и определяемые через ключевое слово &KEY в лямбда-списке, также являются самоооределенными формами.

2) Символы, которые используются в качестве переменных.

3) Формы в виде списочной структуры, которыми являются:

a) Вызовы функций и лямбда-вызовы.

b) Специальные формы (special form), в число которых входят SETQ, QUOTE и другие, предназначенные для управления вычислением и контекстом.

c) Макровызовы (рассмотрим немного позднее).

У каждой формы свой синтаксис и семантика, основанные, однако, на едином способе записи и интерпретации.

 

 

Управляющие структуры Лиспа являются формами

В распространенных процедурных языках наряду с основными действиями есть специальные управляющие механизмы разветвления вычислений и организации циклов. В Паскале, например, используются структуры IF THEN ELSE, WHILE DO, CASE и другие.

Управляющие структуры Лиспа (мы будем для них использовать термин предложение (clause)) выглядят внешне как вызовы функций. Предложения будут записываться в виде скобочных выражений, первый элемент которых действует как имя управляющей структуры, а остальные элементы – как «аргументы». Результатом вычисления, так же как у функции, является значение, т.е. управляющие структуры представляют собой формы. Однако предложения не являются вызовами функций, и разные предложения используют аргументы по-разному.

Наиболее важные с точки зрения программирования синтаксические формы можно на основе их использования разделить на следующие группы:

 

1. Работа с контекстом:

- QUOTE или блокировка вычислений;

- Вызов функции и лямбда-вызов;

- Предложения LET и LET*

 

2. Последовательное исполнение:

- предложения PROG1, PROG2 и PROGN.

3. Разветвление вычислений:

- условные предложения COND, IF, WHEN, UNLESS;

- выбирающее предложение CASE.

4. Итерации:

- циклические предложения DO, DO*, LOOP, DOTIMES, DOUNTIL.

 

5. Передачи управления:

- предложения PROG, GO и RETURN.

 

6. Динамическое управление вычислением:

- THROW и CATCH, а также BLOCK.

 

Ранее мы уже рассмотрели форму QUOTE, а также лямбда-вызов и вызов функции. Эти формы тесно связаны с механизмом определения функций и их вызова. Остальные формы в основном используются в теле лямбда-выражений, определяющих функции.

 

 

LET создает локальную связь

Вычисление вызова функции создает на время вычисления новые связи для формальных параметров функции. Новые связи внутри формы можно создать и с помощью предложения LET. Эта структура (немного упрощенно) выглядит так:

 

(LET ((m1 знач1) (m2 знач2) . . .)

форма1

форма2 . . .)

Предложение LET вычисляется так, что сначала статические переменные m1, m2, . . . из первого «аргумента» формы связываются (одновременно) с соответствующими значениями знач1, знач2, . . . Затем слева направо вычисляются значения форм форма1, форма2, . . . В качестве значения всей формы возвращается значение последней формы. Как и у функций, после окончания вычисления связи статических переменных m1, m2, . . . ликвидируются и любые изменения их значений (SETQ) не будут видны извне.

Например:

 

_(setq x 2)

_(let ((x 0)) (setq x 1))

_x

Форма LET является на самом деле синтаксическим видоизменением лямбда-вызова, в которой формальные и фактические параметры помещены совместно в начале формы:

 

(LET ((m1 a1) (m2 a2) . . . (mn an))

форма1 форма2 . . .)

ó

((LAMBDA

(m1 m2 . . . mn) ; формальные параметры

форма1 форма2 . . .) ; тело функции

а1 а2 . . .an) ; фактические параметры

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

Значения переменным формы LET присваиваются одновременно. Это означает, что значения всех переменных mi вычисляются до того, как осуществляется связывание с формальными параметрами. Новые связи этих переменных еще не действуют в момент вычисления начальных значений переменных, которые перечислены в форме позднее.

Например:

 

_(let ((x 2) (y (* 3 x)))

(list x y)) ; при вычислении Y

Error: Unbound atom X ; у Х нет связи

Побочный эффект можно наблюдать при работе с формой LET* подобной LET, но вычисляющей значения переменных последовательно:

 

_(let* ((x 2 ) (y (* 3 x)))

(list x y))

(2 6)

Последовательные вычисления: PROG1, PROG2 и PROGN

Предложения PROG1, PROG2 и PROGN позволяют работать с несколькими вычисляемыми формами:

 

(PROG1 форма1 форма2 . . . формаN)

(PROG2 форма1 форма2 . . . формаN)

(PROGN форма1 форма2 . . . формаN)

У этих специальных форм переменное число аргументов, которые они последовательно вычисляют и возвращают в качестве значения значение первого (PROG1), второго (PROG2) или последнего (PROGN) аргумента. Эти формы не содержат механизма определения внутренних переменных:

 

_(prong (setq x 2) (setq y (* 3 x)))

_x

Многие формы, как, например описанная выше форма LET(*), позволяют использовать последовательность форм, вычисляемых последовательно, и в качестве результата последовательности возвращают значение последней формы. Это свойство называют неявным PROGN (implicit prong feature).

 

 

Разветвление вычислений: условное предложение COND

Предложение COND является основным средством разветвления вычислений. Это синтаксическая форма, позволяющая управлять вычислениями на основе определяемых предикатами условий. Структура условного предложения такова:

 

(COND (p1 a1)

(p2 a2)

. . .

(pN aN))

Предикатами pi и результирующими выражениями ai могут быть произвольные формы. Значение предложения COND определяется следующим образом:

1. Выражения pi, выполняющие роль предикатов, вычисляются последовательно слева направо (сверху вниз) до тех пор, пока не встретится выражение, значением которого не является NIL, т.е. логическим значением которого является истина;

2. Вычисляется результирующее выражение, соответствующее этому предикату, и полученное значение возвращается в качестве значения всего предложения COND;

3. Если истинного предиката нет, то значением COND будет NIL.

 

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

В следующем примере с помощью предложения COND определена функция, устанавливающая тип выражения (здесь для примера принимается, что возможны лишь три типа выражений: пустой список, атом и список):

 

_(defun тип (l)

(cond ((null l) ’пусто)

((atom l) ’атом)

(t ’список)))

ТИП

_(тип ’(a b c))

СПИСОК

_(тип (atom ’(а т о м)))

ПУСТО

В условном предложении может отсутствовать результирующее выражение ai или на его месте часто может быть последовательность форм:

 

(COND (p1 a11)

. . .

(pi) ; результирующее

Выражение отсутствует

(pk ak1 ak2 . . .akN) ; последовательность форм

В качестве результата

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

В качестве примера использования условного предложения определим логические действия логики высказываний «и», «или», «не», => (импликация) и < = > (тождество):

 

_(defun и (x y)

(cond (x y)

(t nil)))

И

_(и t nil)

NIL

_(defun или (x y)

(cond (x t)

(t y)))

ИЛИ

_(или t nil)

T

_(defun не (x)

(not x))

НЕ

_(не t)

NIL

_(defun => (x y)

(cond (x y)

(t t)))

=>

_(=> nil t)

T

Импликацию можно определить через другие операции:

 

_(defun => (x y)

(или x (не y)))

=>

_(defun < = > (x y)

(и (=> x y) (=> y x)))

< = >

Предикаты «и» и «или» входят в состав встроенных функций Лиспа и называются AND и OR. Число их аргументов может быть произвольным.

 

_(and (atom nil) (null nil) (eq nil nil))

T

Предикат AND в случае истинности возвращает в качестве значения значение своего последнего аргумента. Его иногда используют как упрощение условного предложения по следующему образцу:

 

(AND условие1 условие2 . . . условиеN)

ó

(COND ((AND условие1 условие2 . . . условиеN-1)

условиеN)

(T NIL))

_(and (atom nil) (+ 2 3))

Такое использование предиката AND не рекомендуется.

Предложения COND можно комбинировать таким же образом, как и вызовы функций. Например, предикат «исключающее или», который является ложным, когда оба аргумента одновременно либо истинны, либо нет, можно определить следующим образом:

 

_(defun xor (x y)

(cond (x (cond (y nil)

(t t)))

(t y)))

XOR

_(xor t nil)

T

_(xor nil nil)

NIL

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

 

 







Последнее изменение этой страницы: 2016-09-05; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.209.10.183 (0.014 с.)