Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Conspekt.doc
Скачиваний:
11
Добавлен:
31.08.2019
Размер:
1.39 Mб
Скачать

2.2.20 Prog- механизм.

Тот, кто знаком с алгоритмическими языками, ощущает некоторое неудобство при программировании на языке ЛИСП, вызванное отсутствием столь привычных средств записи программ, как оператор присваивания, оператор перехода, метки, описания переменных. При отсутствии опыта может получиться, что писать программы на ЛИСПе вообще невозможно. Конечно, это не так, но подобный стиль программирования вызывает некоторые трудности как практического, так и психологического характера. Поэтому в язык включен аппарат программирования, представляющий все упомянутые возможности. Общий вид выражения, приводящего этот аппарат в действие таков:

(PROG (U1 U2 ... Un) S1 S2 ...Sm),

где U1,U2,...,Un- переменные, которые в обычном программировании соответствовали бы локальным переменным. Список играет роль описания переменных, т.е. указывает, какими новыми переменными мы хотим пользоваться в данном обращении к PROG. Если нет локальных переменных, то этот список является пустым и записывается в виде (). Тогда все переменные введенные в PROG механизме сохраняют свои значения;

S1,S2,...,Sm- s-выражения, которые называются операторами. Если s-выражение- атом, то атом понимается как метка. В качестве s-выражения могут использоваться функции, описанные ранее. Кроме этого дополнительно только в рамках PROG - механизма могут использоваться следующие функции.

(RETURN e)- возврат из PROG. Результат-значение выражения - e.

(GO l)- переход на метку, где l- атом, встречающийся среди операторов S1,S2,...,Sm.

Среди S1,S2,...,Sm может быть (COND (P1 t1)(P2 t2)...) - условный оператор, отличающийся от условного выражения только тем, что ti- операторы, в т.ч. GO,RETURN,SETQ,COND и т.д.

Выполнение PROG состоит в том, что всем программным переменным U1,...,Un присваивается начальное значение NIL и затем начинают выполняться операторы S1,...,Sm. Этот порядок может быть изменен с помощью оператора перехода.

Обращения к функции PROG применяются чаще всего в составе определяющего выражения в качестве его тела, причем функции, использующие такое описание, выполняются иногда быстрее. В качестве примера определим функцию MEMB, используя PROG-механизм:

(DEFUN MEMB_PROG(X Y)

(PROG ()

A: (COND

((NULL Y) (RETURN NIL))

((EQ X (CAR Y))(RETURN T))

)

(SETQ Y (CDR Y))

(GO A)

)

)

В muLisp имеется специальная функция для организации итерационных циклов:

(LOOP

задача 1

. . .

задача n )

при выполнении n-ой задачи управление опять передается 1-й задаче, до тех пор, пока из условных задач одна не станет истинной.

(DEFUN MEMB_LOOP (X Y)

(LOOP

((NULL Y) NIL)

((EQ X (CAR Y)))

(SETQ Y (CDR Y))

)

)

2.3 Обращение (инверсия) списков

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

Пусть имеется список (A B (C (D E)) F). Необходимо записать его в обратном порядке, включая инверсию всех внутренних списков, т.е. получить результат (F((E D) C) B A).

Чтобы это выполнить, введем переменные X и Y:

X - будет обозначать еще не обработанную часть списка

Y- уже обработанную часть списка с переставленными элементами (промежуточный результат).

Тогда на некотором этапе выполнения программы возможна ситуация:

X=((C (D E)) F)

Y=(B A)

В начале работы список Y пуст, а список Х соответствует исходному. Когда Х окажется пустым, в Y будет находиться требуемый результат.

Если же Х не пуст, то следует выделить из него первый элемент, перевернуть его, если он не атом, и вставить его в начало списка Y.

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

По условию, надо определить функцию REV, которая будет обращать список. Определим вспомогательную функцию REV1, над аргументами X и Y, которая будет выполняться выше описанные действия (рис. ).

REV 1

+ -

NULL

V X?

Y + -

ATOM

Y=Y+(REV (CAR X))

X=(CDR X)

Y=Y+(CAR X)

X=(CDR X)

CAR

X

Рис.2.1 Структурная схема алгоритма обращения списка

Текст программы:

(DEFUN REV1 (X Y)

(COND

((NULL X) Y)

((ATOM (CAR X))(REV1 (CDR X)(CONS (CAR X) Y)))

(T (REV1 (CDR X)(CONS (REV (CAR X)) Y)))

))

Теперь определим функцию REV.

(DEFUN REV (X)

(REV1 X NIL))

Решим аналогичную задачу по-другому, используя механизм PROG.

(DEFUN REV (X)

(PROG (U Y)

A (COND ((NULL X)(RETURN Y)))

(SETQ U (CAR X))

(SETQ X (CDR X))

(COND ((NULL (ATOM U))(SETQ U (REV U))))

(SETQ Y (CONS U Y))

(GO A)

))

Переменная У свое начальное значение NIL получает автоматически (см. PROG).

В этом примере обращение на внешнем уровне выполняется с помощью перехода на метку, а обращение на внутреннем уровне выполняется с помощью рекурсивного обращения.

Рассмотрим в учебных целях еще один возможный способ определения функции REV.

(DEFUN REV (X)

(COND ((ATOM X) X)

( T (PROG (U)

A (COND ((NULL X)(RETURN U)))

(SETQ U (CONS (REV (CAR X)) U))

(SETQ X (CDR X))

(GO A)

)

)

)

)

Последний вариант имеет самую короткую запись, но в нем слишком много времени тратится на проверку того, что очередной элемент списка атом и его не нужно инвертировать.

Далее с целью закрепления навыков написания рекурсивных функций рассмотрим типовые функции обработки списков.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]