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

От ФП к ООП

< Лекция 9 || Лекция 10: 12345 || Лекция 11 >

Множественное наследование

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

При реализации такой идеи не следует проверять объект ранее его последователей ( a b c d ):

(d)
        / \   
    (b)    (c) 
        \ /
        (a)

Если это сделать прямым сцеплением списков, то результат слишком неэффективен.

Определяемые объекты

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

Функциональный синтаксис

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

Экземпляры

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

Векторная реализация

Реализация ООП с помощью хэш-таблиц обладает слегка парадоксальной окраской: гибкость у нее больше, чем надо, и за большую цену, чем можно позволить. Уравновесить это может подобная реализация на базе простых векторов. Этот переход показывает, как функциональное программирование дает новое качество "на лету". В опорной реализации фактически не было реализационного разделения объектов на экземпляры и классы. Экземпляр — это был просто класс с одним-единственным предком. При переходе к векторной реализации разделение на классы и экземпляры становится реальным. В ней становится невозможным превращать экземпляры в классы простым изменением свойства.

Еще одна реализация

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

DEFUN classes (cl) (COND 
                   (cl (CONS (cdar cl) (classes (CDR cl)))) ))

 
    ; вывод формулы классов аргументов из определения 
    ; параметров метода
    ; NIL — произвольный класс
 
    (DEFUN argum (cl) (COND 
                 (cl (CONS (caar cl) (argum (CDR cl)))) ))
 
    ; вывод списка имен аргументов из определения 
    ; параметров метода
 
    (defun defmet (FMN c-as expr)
       (setf (get FMN 'category) 'METHOD)
       (setq ML (cons(cons(cons FMN (classes c-as))
          (list 'lambda (argum c-as) expr) ) ML))
    FMN )
    ; объявление метода и расслоение его определения
    ; для удобства сопоставления с классами аргументов
 
    (DEFUN defcl (NCL SCL FCL ) 
    ; имя, суперкласс и поля/слоты класса
       (SETQ ALLCL (CONS NCL ALLCL))
       (SET NCL (append FCL SCL)) )
 
    ; значением класса является список его полей, 
    ; возможно, со значениями
 
    (DEFUN ev-cl (vargs) (COND 
 
    ; вывод формата фактических аргументов для поиска 
    ; метода их обработки
 
       (vargs (CONS (COND 
             ((member (caar vargs) ALLCL) 
               (caar vargs)) )
          (ev-cl (CDR vargs)))) ))
    ; NIL если не класс
 
    (DEFUN m-assoc (pm meli) (COND 
               (meli (COND ((equal  (caar meli) pm)
                                   (cdar meli))
             (T (m-assoc pm (CDR meli)))))))
 
    ; поиск подходящего метода, соответствующего 
    ; формату классов данных
 
    (DEFUN method (MN args &optional c) 
       (APPLY (m-assoc (CONS mn (ev-cl args)) ML)
               args c))

     ; если метода не нашлось, в программе следует 
    ; выполнить приведение 
    ; параметров к нужному классу
 
    (DEFUN instance (class &optional cp) (COND
 
    ; подобно Let безымянная копия контекста
 
       (DEFUN instance (class &optional cp) (COND
    ; подобно Let безымянная копия контекста
 
       (class (COND ((ATOM (CAR class))
                     (instance (CDR class) cp))
                    ((assoc (caar class) cp)
                     (instance (CDR class) cp))
                   (T(instance (CDR class) 
                               (CONS (CAR class)cp))) 
    )) ) cp)
 
    (DEFUN slot (obj fld) (assoc fld obj))
 
    ; значение поля объекта

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

(DEFUN evcon- (c &optional a)
    ;                  |_________ключ, объявляющий 
    ;                                   необязательные параметры
       (COND
       ((eval-p (CAR (CAR c)) a) 
        (eval-p (CAR (CDR (CAR c))) a) )
       (T (evcon- (CDR c) a) ) ))
 
    (DEFUN evlis- (m &optional a)
       (COND
          ((EQ m NIL) NIL)
          ( T (CONS (eval-p (CAR m) a) 
                    (evlis- (CDR m) a) ) ) ))
 
(DEFUN eval-p (e &optional c)
    (COND ((ATOM e) (value e c))
          ((ATOM (CAR e))
           (COND 
 
             ((EQ (CAR e) 'QUOTE) (CAR (CDR e)))
             ((EQ (CAR e) 'COND) (evcon- (CDR e) a))
             ((get (CAR e) 'METHOD) 
             (method (CAR e) (evils (CDR e)) c) )
          (T (apply-p (CAR e)(evlis- (CDR e) c) c))
      ) )
        (T (apply-p (CAR e)(evlis- (CDR e) c) c))
) ) 

(DEFUN apply-p (f args &optional c)
    (COND ((ATOM f) (apply-p (function f c) args c))
          ((ATOM (CAR f))
           (COND ((get (CAR f) 'macro)
                  (apply-p (apply-p (get (CAR f) 'macro)
                                    (CDR f) c)
                            args c))
 
                (T(apply-p (eval f c) args c))
           ) )
        (T (apply-p (eval f c) args c))
 ))
 
    (print (eval-p 1))
    (print (eval-p 'a))
    (print (eval-p '(QUOTE b)))
    (print (eval-p '(COND (NIL 6)(T 88) )))
    (print (eval-p '(CAR '(3 2))))
< Лекция 9 || Лекция 10: 12345 || Лекция 11 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

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

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

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