Опубликован: 21.08.2007 | Доступ: свободный | Студентов: 1787 / 192 | Оценка: 4.23 / 3.74 | Длительность: 15:37:00
Лекция 2:

Определение языков программирования

< Лекция 1 || Лекция 2: 1234 || Лекция 3 >

Универсальная функция eval, которую предстоит определить, должна удовлетворять следующему условию: если представленная аргументом форма сводится к функции, имеющей значение на списке аргументов этой же формы, то данное значение и является результатом функции eval.

(eval '(fn arg1 ... argK)) = результат применения fn 
                                     к аргументам arg1, ..., argK.

Явное определение такой функции позволяет достичь четкости механизмов обработки Лисп-программ.

(eval '((LAMBDA (x y) (CONS (CAR x) y))
         '(A B) '(C D) ))
        = (A C D)

Вводим две основные функции eval и apply для обработки форм и обращения к функциям, соответственно. Каждая из этих функций использует ассоциативный список для хранения связанных имен - значений переменных и определений функций. Сначала этот список содержит лишь определения встроенных констант.

Вернемся к синтаксической сводке вычислимых форм в примере 2.2.

Каждой ветви этой сводки соответствует ветвь универсальной функции:

(DEFUN eval (e)   (evl e '((Nil . Nil) (T . T))))

Вспомогательная функция evl понадобилась, чтобы в eval ввести контекст - накапливающий параметр, в котором будут храниться связи между переменными и их значениями и названиями функций и их определениями. При определении evl ранее выбранные распознаватели и селекторы погружаем в более общее ветвление COND3 (if pr tr fl) эквивалентно (cond (pr tr) (T fl)), где T - тождественная истина. и каждому из них сопоставляем свой семантический обработчик, выполняющий действие, соответствующее смыслу распознанной конструкции4Точный смысл использованных в определении функций можно посмотреть в лекции, посвященной функциональному программированию. :

(DEFUN evl(e a) (COND
                ( (atom e)                     (cdr(assoc e a)) )
                ( (eq (car e) 'QUOTE)  (cadr e))
                ( (eq(car e) 'IF)             (evcon(cdr e) a))
                ( T                   (apply (car e) (evlis(cdr e) a) a) ))    )

(defun apply (fn x a) (COND 
                     ((atom fn)(cond 
                  ((eq fn 'CAR)   (caar x))
                  ((eq fn 'CDR)   (cdar x))
                  ((eq fn 'CONS) (cons (car x)(cadr x)) )
                  ((eq fn 'ATOM)(atom (car x)) )
                  ((eq fn 'EQ)      (eq (car x)(cadr x)) )
                                         (T               (apply (evl fn a) x a)) ) )
                      )
((eq(car fn)'LAMBDA) (eval (caddr fn) 
                  (pairlis (cadr fn) x a) ))
((eq (car fn) 'LABEL) (apply (caddr fn) x 
                   (cons (cons (cadr fn)(caddr fn)) a)))))

(DEFUN evcon (c a) (COND 
                   ((evl (car  c) a) (evl (cadr c) a) )
                   ( T  (evl (caddr c) a)     ) ))

(DEFUN evlis (m a) (COND
                   ((null m) Nil )
                     ( T          (cons(evl (car m) a) 
                              (evlis(cdr m) a)    ))   )

Поясним ряд пунктов этих определений.

Первый аргумент evl - форма. Если она - атом, то этот атом может быть только именем переменной, а значение переменной должно уже находиться в ассоциативном списке.

Если CAR от формы - QUOTE, то она представляет собой константу, значение которой выбирается селектором CADR от нее самой.

Если CAR от формы - IF, то форма - условное выражение. Вводим вспомогательную функцию EVCON, которая обеспечивает вычисление предиката и выбор формы, соответствующей значению предиката. Эта форма передается EVL для дальнейших вычислений.

Все остальные случаи рассматриваются как список из функции с последующими аргументами.

Вспомогательная функция EVLIS обеспечивает вычисление аргументов, затем представление функции и список вычисленных значений аргументов передаются функции APPLY.

Первый аргумент apply - функция. Если она - атом, то существует две возможности. Атом может представлять одну из элементарных функций ( car cdr cons atom eq ). В таком случае соответствующая ветвь вычисляет значение этой функции на заданных аргументах. В противном случае, этот атом - имя ранее заданного определения, которое можно найти в ассоциативном списке, подобно вычислению переменной.

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

Если функция начинается с LABEL, то ее название и определение соединяются в пару, и полученная пара размещается в ассоциативном списке, чтобы имя функции стало определенным при дальнейших вычислениях. Они произойдут как рекурсивный вызов apply, которая вместо имени функции теперь работает с ее определением при более полном ассоциативном списке - в нем уже размещено определение имени функции. Поскольку определение размещается "наверху" стека, оно становится доступным для всех последующих переопределений, то есть работает как локальный объект.

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

  1. В строгой теории языка программирования все функции следует определять всякий раз, когда они используются. На практике это неудобно. Реальные системы имеют большой запас встроенных функций, известных языку, и возможность присоединения такого количества новых функций, какое понадобится.
  2. Для языков программирования характерно большое разнообразие условных форм, конструкций выбора, ветвлений и циклов, практически без ограничений на их комбинирование.
  3. В реальных системах программирования обычно поддерживается работа с целыми, дробными и вещественными числами в предельно широком диапазоне, а также работа с кодами и строками. Такие данные, как и атомы, являются минимальными объектами при обработке информации, но отличаются от атомов тем, что их смысл задан непосредственно их собственным представлением.

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

Составляющие этой формальной системы следующие:

  1. Множество символов, называемых списками.
  2. Система функциональных обозначений для основных понятий, необходимых при программировании обработки списков.
  3. Формальное представление функциональных обозначений в виде списков.
  4. Универсальная функция (записанная в виде списка), интерпретирующая обращение произвольной функции, записанной как списка, к ее аргументам.
  5. Система базовых функций, обеспечивающих техническую поддержку обработки списков, и специальных функций, обеспечивающих управление вычислениями.

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

Для полноты картины приведем ряд вспомогательных определений, показывающих механизмы определения обычной, процедурной техники программирования с присваиваниями и глобальными переменными:

APPEND - функция двух аргументов x и y, сцепляющая два списка в один.

(DEFUN append (x y) (IF 
    (null x) y (CONS
        (CAR x) 
        (append (CDR x) y)        
)))

INSERT - вставка z перед вхождением ключа x в список al.

(DEFUN insert (al x z) (COND 
        ((null al) Nil)
        ((equal (CAR al) x) (CONS z al))
        ((QUOTE T) (CONS (CAR al) (insert (CDR al) x z)))
))

(insert '(a b c) 'b 's)  =  (a s b c)

ASSIGN - модель присваивания переменным, хранящим значения в ассоциативном списке. Замена связанного с данной переменной в первой паре значения на новое заданное значение. Если пары не было вообще, то новую пару из переменной и ее значения помещаем в конец списка, чтобы она могла работать как глобальная.

(DEFUN assign (x v al) (COND 
         ((Null al) (CONS (CONS x v) Nil ))
         ((equal x (CAAR al))(CONS (CONS x v) (CDR al)))
        ((QUOTE T) (CONS (CAR al) (assign x v (CDR al))))
))

(assign 'a 111 '((a . 1)(b . 2)(a . 3))) = ((a . 111)(b . 2)(a . 3))
(assign 'a 111 '((c . 1)(b . 2)(a . 3))) = ((c . 1)(b . 2)(a . 111))
(assign 'a 111 '((c . 1)(d . 3)))          = ((c . 1)(d . 3) (a . 111))

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

В дальнейших лекциях будут рассмотрены наиболее известные модели различных парадигм программирования. Начиная с низкоуровневого программирования на ассемблере дойдем до суперпрограммирования на языках сверх высокого уровня.

< Лекция 1 || Лекция 2: 1234 || Лекция 3 >
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?