Опубликован: 05.02.2007 | Уровень: для всех | Доступ: платный | ВУЗ: Новосибирский Государственный Университет
Лекция 11:

Стандартное программирование

< Лекция 10 || Лекция 11: 12 || Лекция 12 >
Аннотация: Эта лекция посвящена привычным методам программирования. Для этого в Лисп введена функциональная модель средств императивного программирования в виде специальной функции Prog, а также специальных функций для представления в программах на Лиспе присваиваний и циклов.

Императивное программирование

Противопоставление функционального и императивного (операторно-процедурного) стилей программирования порой напоминает свифтовские бои остроконечников с тупоконечниками. Впрочем, переписать функциональную программу в императивную проще, чем наоборот.

С практической точки зрения любые конструкции стандартных языков программирования могут быть введены как функции. Это делает их вполне легальными средствами в рамках функционального подхода. Надо лишь четко уяснить цену такого дополнения и его преимущества, обычно связанные с наследованием решений или с привлечением пользователей. В первых реализациях Лиспа были сразу предложены специальные формы и структуры данных, служащие мостом между разными стилями программирования. Они заодно смягчали на практике недостатки упрощенной схемы интерпретации S-выражений, выстроенной для учебных и исследовательских целей. Важнейшие средства такого рода, выдержавшие испытание временем, - prog-форма, списки свойств атома и деструктивные операции. В результате язык программирования расширяется так, что становятся возможными оптимизирующие преобразования структур данных, программ и процессов и раскрутка систем программирования.

Prog-форма

Рассмотрим предложенный МакКарти пример1Применение prog-выражений позволяет писать паскалеподобные программы, состоящие из операторов, предназначенных для исполнения. (Точнее "алголоподобные", т.к. появились лет за десять до паскаля. Но теперь более известен паскаль.) показывающий возможности prog-формы при императивном стиле определения функции Length. Эта функция сканирует список и вычисляет число элементов на верхнем уровне списка. Значение функции Length - целое число. Алгоритм можно описать следующими словами:

"Это функция одного аргумента L.
 Она реализуется программой с двумя рабочими переменными z и v.
        Записать число 0 в v.
        Записать аргумент L в z.
A: Если z содержит NIL, то программа выполнена 
         и значением является то, 
         что сейчас записано в v.
       Записать в z cdr от того, что сейчас в z.
       Записать в v на единицу больше того, что сейчас записано в v.
 Перейти к A"

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

function LENGTH (L: list) : integer;

             var Z: list; 
                 V: integer;
begin
      V := 0;
      Z := l;
A:     if null (Z) then LENGTH := V;       
      Z := cdr (Z);
      V := V+1;
             goto A;
end;

Переписывая в виде S -выражения, получаем программу:

(defun      
LENGTH (lambda (L)
                  (prog (Z V)
      (setq V 0)
      (setq Z L)
A       (cond  ((null Z)(return V)))
      (setq Z (cdr Z))
      (setq V (+ 1 V))
(go  A)  )))  ))

;;=======================ТЕСТЫ=============
(LENGTH '(A B C D))
(LENGTH '((X . Y) A CAR (N B) (X Y Z)))

Последние две строки содержат тесты. Их значения 4 и 5 соответственно.

Форма Prog имеет структуру, подобную определениям функций и процедур в Паскале: ( PROG, список рабочих переменных, последовательность операторов и атомов ... ) Атом в последовательности выполняет роль метки, локализующей оператор, расположенный вслед за ним. В вышеприведенном примере метка A локализует оператор, начинающийся с " COND ".

Первый список после символа PROG называется списком рабочих переменных. При отсутствии таковых должно быть написано NIL или (). С рабочими переменными обращаются примерно как со связанными переменными, но они не могут быть связаны ни с какими значениями через lambda. Значение каждой рабочей переменной есть NIL, до тех пор, пока ей не будет присвоено что-нибудь другое.

Присваивания

Для присваивания переменной применяется форма SET. Чтобы присвоить переменной pi значение 3.14 пишется:

(SET (QUOTE PI)3.14)

SETQ подобна SET, но она еще и блокирует вычисление первого аргумента. Поэтому

(SETQ PI 3.14)

запись того же присваивания. SETQ обычно удобнее. SET и SETQ могут изменять значения любых переменных из ассоциативного списка более внешних функций. Значением SET и SETQ является значение их второго аргумента.

GO-форма, используемая для указания перехода (GO A) указывает, что программа продолжается оператором, помеченным атомом A, причем это A может быть и из более внешнего prog.

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

RETURN - нормальное завершение программы. Аргумент return вычисляется, что и является значением программы. Никакие последующие операторы не вычисляются.

Если программа прошла все свои операторы, не встретив Return, она завершается со значением NIL.

< Лекция 10 || Лекция 11: 12 || Лекция 12 >
Роман Храпай
Роман Храпай
Россия
Роман Храпай
Роман Храпай
Украина