Опубликован: 19.03.2004 | Уровень: специалист | Доступ: платный
Лекция 6:

Свойства атомов и категории функций

< Лекция 5 || Лекция 6: 12345 || Лекция 7 >
Аннотация: Методы расширения функциональных построений применены для моделирования привычного операторно-процедурного стиля программирования и техники работы с глобальными определениями. Демонстрируется еще один важный метод — обобщение базовой схемы обработки символьных выражений и представленных с их помощью функциональных форм на основе списков свойств атомов. В результате можно собирать и специализировать функционально полное определение гибкого и расширяемого интерпретатора для языка программирования на примере Лиспа, написанном на Лиспе. Акцент на возможности варьирования семантики функций и пополнения семантического базиса с целью автоматизации выполненных построений в процессе исследования границ класса решаемых задач и конкретизации методов их решения.
Ключевые слова: императивность, работ, функциональная форма, интерпретация, абстрактная машина, функциональный подход, Лисп, prog-форма, деструктивная операция, Паскаль, определение, length, список, значение, целое число, рабочая переменная, cond, nil, set, запись, внешняя функция, оператор, вычисление, go, программа, return, функция, rev, поиск, эффективная реализация, присваивание, атом, указатель, тэг, индикатор, поле, печатное имя атома, определяющее выражение, машинный язык, постоянное значение, Lisp, структура данных, очередь, GET, defun, remprop, CDR, Common Lisp, Clisp, представление, мусорщик, псевдо-функция, деструктивная функция, блок памяти, список свободной памяти, эффективный язык, базисные функции, CONS, чистый Лисп, ассоциативный список, rplaca, rplacd, основная память, циклический список, mapc, prog2, активный список, частичный результат, линейное выметание осовободившегося пространства, поле свободной памяти, поле полных слов, удобочитаемость, подвыражение, функциональный аргумент, macro, категория функции, механизмы, параметр, ключевой параметр

Prog-выражения и циклы

Противопоставление функционального и императивного стилей программирования порой напоминает войну остроконечников с тупоконечниками. Существует большое число чисто теоретических работ, исследовавших соотношения между потенциалом того и другого подхода и пришедших к заключению о формальной сводимости в обе стороны при некоторых непринципиальных ограничениях на технику программирования. Методика сведения императивных программ в функциональные заключается в определении правил разметки или переписывания схемы программы в функциональные формы. Переход от функциональных программ к императивным технически сложнее: используется интерпретация формул над некоторой специально устроенной абстрактной машиной [3, 4]. На практике переложение функциональных программ в императивные выполнить проще, чем наоборот — может не хватать близких понятий.

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

Применение prog-выражений позволяет писать "паскалеподобные" программы, состоящие из операторов, предназначенных для исполнения. (Точнее "алголоподобные", т.к. появились лет за десять до паскаля. Но теперь более известен паскаль.)

Для примера prog-выражения приводится императивное определение функции ( LENGTH *), сканирующей список и вычисляющей число элементов на верхнем уровне списка. Значение функции >LENGTH целое число. Программу можно примерно описать следующими словами (Стилизация примера от МакКарти [ 1 ] .):

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

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

function LENGTH (L: list) : integer;
label A;
   var U: list;
         V: integer;
begin
     V := 0;
     U := L;
A: if null (U) then LENGTH := V; 
      U := cdr (U);
      V := V+1;
   goto A;
end;

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

((DEFUN LENGTH  (L)
  (PROG (U V)
        (SETQ V 0)
        (SETQ U L)
A   (COND ((NULL U)(RETURN V)))
        (SETQ U (CDR U))
        (SETQ V (+ 1 V))
    (GO A) 
 
)  )
 
(LENGTH '(A B C D))
(LENGTH '((X . Y) A CAR (N B) (X Y Z)))

Последние две строки содержат тесты. Их значения четыре и пять, соответственно. 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 .

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

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

Формы GO, SET, RETURN могут применяться как операторы лишь на верхнем уровне PROG или внутри COND, находящегося на верхнем уровне PROG.

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

Prog-выражение, как и другие Лисп-функции, может быть рекурсивным.

Функция REV, обращающая список и все подсписки, столь же естественно пишется с помощью рекурсивного Prog-выражения.

function rev (x: list) :List;  
   label A, B;
   var y, z: list;
begin

A: if null (x) then rev := y;
       z := cdr (x);
    if atom (z) then goto B;
       z := rev (z);

B:    y := cons (z, y);
       x := cdr (x);
    goto A;
end;

Функция rev обращает все уровни списка, так что rev от (A ((B C) D)) даст ((D (C B))A).

(DEFUN REV (X) 
     (PROG (Y Z)

A    (COND ((NULL X)(RETURN Y)))
          (SETQ Z (CDR X))
     (COND ((ATOM Z)(GOTO B)))
          (SETQ Z (REV Z))
 
B         (SETQ Y (CONS Z Y))
           (SETQ X (CDR X))
   (GOTO A)
))

Для того чтобы форма PROG была полностью законна, необходима возможность дополнять а-список рабочими переменными. Кроме того, операторы этой формы требуют специального расширения языка — в него включаются формы GO, SET и RETURN, не известные вне PROG. Атомы, играющие роль меток, работают как указатели помеченного блока.

Кроме того, уточнен механизм условных выражений: отсутствие истинного предиката не препятствует формированию значения COND -оператора, т.к. все операторы игнорируют выработанное значение. Это позволяет считать, что значением является NIL. Такое доопределение условного выражения приглянулось и в области обычных функций, где оно часто дает компактные формулы для рекурсии по списку.

В принципе, SET и SETQ могут быть реализованы с помощью a-списка примерно так же, как и поиск значения, только с копированием связей, расположенных ранее изменяемой переменной (см. функцию ASSIGN из лекции 3). Более эффективная реализация будет описана ниже.

(DEFUN SET (X Y) (ASSIGN  X Y Alist))

Обратите внимание, что введенное таким образом присваивание работает разнообразнее, чем традиционное: обеспечена вычисляемость левой части присваивания, т.е. можно в программе вычислять имена переменных, значение которых предстоит поменять.

(SETQ X 'Y)
(SET X 'NEW)
(PRINT X)
(PRINT Y)
6.1.

Напечатается Y и NEW (традиционно атомы печатаются заглавными буквами).

< Лекция 5 || Лекция 6: 12345 || Лекция 7 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?

Алина Ленкова
Алина Ленкова
Россия, Ставрополь, СФ МГУПИ, 2014
Валерий Ромашов
Валерий Ромашов
Россия