Сборка программы из автономно развивающихся компонентов опирается на формулировку достигаемой ими цели, понимание которой гарантирует не только корректность полученного результата, но и рациональность его использования. Формулировать цели частей программы — процесс нетривиальный. В его основе лежат весьма различные подходы к классификации понятий.
Текст программы одновременно представляет и динамику управления
ООП структурирует множество частных методов, используемых в программе, в соответствии с иерархией классов объектов, обрабатываемых этими
При
Типичная гипотеза при программировании работы с
Объект не изменен, если на него не было воздействий из программы.
Но реальность зачастую требует понимания и учета более сложных обстоятельств, что может существенно продлить время жизни программы или ее компонентов. В таком случае удобно предоставлять
Дальнейшее развитие подходов к декомпозиции программ связано с выделением отдельных проекций и шагов при решении сложных задач. Понятие проекции связано с различием точек зрения, позволяющим описывать решение всей задачи, но отражать в описании только видимые детали. По мере изменения точек зрения могут проступать новые детали, до тех пор, пока дальнейшая детализация не утрачивает смысл, т.е. улучшение трудно заметить или цена его слишком высока. Так, представление символьной информации в Лиспе выделено в отдельную проекцию, независимую от распределения памяти, вычисление значений четко отделено от компиляции программ, понятие связывания имен с их определениями и
Понятие шага обычно связывается с процессом раскрутки программ, оправданным в тех случаях, когда целостное решение задачи не может гарантировать получение приемлемого результата в нужный срок — это влечет за собой непредсказуемо большие трудозатраты.
Удобный подход к организации программ "отдельная работа отдельно программируется и отдельно выполняется" успешно показал себя при развитии операционной системы UNIX [21] как работоспособный принцип декомпозиции программ. Но существуют задачи, например
При
Выбор конкретных шагов можно соотнести с декомпозицией определения языка программирования на синтаксические и семантические, функциональные и машинно-ориентированные, языково-ориентированные и системные аспекты. При такой декомпозиции можно на первых шагах как бы "снять" синтаксическое и семантическое разнообразие языка, как имеющее чисто технический характер. Именно в этом смысл выделения элементарного Лиспа. Такая методика может быть успешна при освоении любого класса задач, информацию о котором можно представить в виде частично формализуемых текстовых и графовых форм.
Дальнейшие шаги раскрутки можно упорядочить по актуальности
При переходе от обычного стандартного программирования с
Чтобы сравнить дистанцию с функциональным программированием, рассмотрим самодельный встроенный в
При организации
Рассмотрим программу вычисления площадей ряда геометрических фигур, таких как круги, прямоугольники, звезды и пр. Ее запись в ОО-стиле поначалу может выглядеть непринципиально отличающейся от обычной программы.
Но допустим, понадобилось ввести новый класс — раскрашенные круги. Если
Практически
В
(gethash 'color obj)
Поскольку функции являются данными
(FUNCALLl (gethash 'move obj) obj 10)
Мы можем определить под эту идею синтаксис как в языке
(DEFUN tell (obj message rest args)
(APPLY (gethash messmage obj) obj args))
что позволяет сказать
(tell obj 'move 10)
Фактически успех GETHASH. Таким образом, определение данной функции нам сразу даст все три основные черты
Посмотрим, во что это обойдется в исходном примере.
Мы должны создать два
В
(SETF (gethash 'area circle-class)
#' (LAMBDA (x)
(* pi (expt (rget ' radius x) 2))))
Теперь можно спрашивать о площади круга, она будет вычисляться согласно rget при чтении tell — при вызове
(rget 'radius our-cicle)
(tell our-circle 'area)
Прежде чем улучшать эту программу, надо проверить, что же получилось. Легкость результата — это трюк, но не программистский трюк, а концептуальный. Не будем забывать о том, что
До сих пор речь шла о простом 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))))
Показанный в [7] пример работает по первому аргументу (выбор подходящего делает это на всех аргументах, причем с рядом вспомогательных средств, обеспечивающих гибкий перебор
(defclass ob () (f1 f2 ...))
Это означает, что каждое вхождение f1 f2 ... ( Слот — это поле записи или списка
(SETF с (make-instance 'ob))
Чтобы задать значение поля, используем специальную функцию:
(SETF (slot-value c) 1223)
До этого значения полей были не определены.
Простейшее определение слота — это его имя. Но в общем случае слот может содержать список
:allocation :class
Изменение такого слота будет доступно всем экземплярам
Нет необходимости все новые слоты создавать в каждом классе.
Пример: ОО-определение
;oop-compile
(defclass expr ()
((type :accessor td)
(sd :accessor ft))
(:documentation "C-expression"))
(defclass un (expr)
; \_____суперкласс для унарных форм
((type :accessor td) ;; можно унаследовать,
(sd :accessor ft)) ;; а здесь не дублировать
(:documentation "quote car *other *adr"))
(defclass bin (expr)
((type :accessor td)
(sd :accessor ft)
(sdd :accessor sd) )
(:documentation "cons + lambda let"))
(defclass trio (expr)
;; (bin) тогда можно не объявлять первые 3 поля
((type :accessor td)
(sd :accessor ft)
(sdd :accessor sd)
(sddd :accessor td) )
(:documentation "if label"))
(defmethod texrp ((x expr) (nt atom))
(SETF (slot-value x 'type) nt)
(SETF (td x) nt) ;;--;; variant
(:documentation "объявляем тип выражения"))
(defmethod spread ((hd (eql 'QUOTE))
(tl expr))
(let ( (x (make-instance 'un)) )
(SETF (ft x) (car tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'QUOTE))
(tl expr))
(list 'LDC tl)
(:documentation "сборка кода"))
(defmethod compl ((hd (eql 'CAR))
(tl expr)) N)
(append (compl(ft tl) N) '(CAR))
(:documentation "сборка кода"))
(defmethod spread ((hd (eql 'CONS))
(tl expr))
(let ( (x (make-instance 'bin)) )
(SETF (ft x) ( CAR tl))
(SETF (sd x) ( cadr tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'CONS))
(tl bin) N )
(append (compl(sd tl) N) (compl(ft tl) N)
'(CONS))
(:documentation "сборка кода"))
(defmethod compl ((hd (eql '+))
(tl bin) N )
(append (compl(ft tl) N) (compl(sd tl) N)
'(ADD))
(:documentation "сборка кода"))
(defmethod spread ((hd (eql 'IF))
(tl expr))
(let ( (x (make-instance 'trio)) )
(SETF (ft x) ( CAR tl))
(SETF (sd x) ( cadr tl))
(SETF (td x) ( caddr tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'IF))
(tl expr) N )
(let ( (then (list (compl(sd tl) N) '(JOIN)))
(else (list (compl(td tl) N) '(JOIN))) )
(append (compl(ft tl) N) (list 'SEL then else)
)
)(:documentation "сборка кода"))
(defmethod parh ((x expt))
(let (ftx (ft x))
(COND
((ATOM ftx) (spread 'ADR ftx))
((member (CAR ftx)
'(QUOTE CAR CONS + IF LAMBDA LABEL LET))
(spread (CAR ftx) (CDR ftx))
(T (spread 'OTHER ftx) ))
)(:documentation "шаг разбора"))
;====test==========
(SETF test1 (make-instance 'expr))
(texpr test1 'expr)
(SETF (slot-value test1 'sd) (READ))
()
(SETF e1 (make-instance 'expr))
(SETF e2 (make-instance 'expr))
(SETF e3 (make-instance 'expr))
(print (tf e2))
(SETF (slot-value e3 'type) 'expr)
(print (tf e3))
(SETF (slot-value e3 'sd) '(QUOTE const))
(defmethod ep ((x expr))
((LAMBDA (xt)
(SETF (slot-value x 'type) xt) )
(CAR (slot-value x 'sd)) ) )
(print (ep e3))
(print (tf e3))
(print (td e3))
(print (sd e3))
(defmethod ep-q ((x (eql 'QUOTE)) (y expr))
(SETF y (make-instance 'un)))
(SETF (slot-value y 'type) 'QUOTE)
(SETF (slot-value y 'sd) y)
(print (tf (e3 'sd)))
(print (tf e1))
(print(SETF (slot-value e1 'type) (tf e1)))
(SETF (slot-value e2 'sd) 'atom1)
(print (tf (sd e2)))
(print(SETF (slot-value e3 'sd) '(QUOTE const)))
(print (tf e3))
, естественно, использует модель
Более интересный вопрос, что же нам еще может дать функциональный стиль и лисповская традиция
Ответу на этот вопрос посвящены три следующие лекции.
Сборка программы из автономно развивающихся компонентов опирается на формулировку достигаемой ими цели, понимание которой гарантирует не только корректность полученного результата, но и рациональность его использования. Формулировать цели частей программы — процесс нетривиальный. В его основе лежат весьма различные подходы к классификации понятий.
Текст программы одновременно представляет и динамику управления
ООП структурирует множество частных методов, используемых в программе, в соответствии с иерархией классов объектов, обрабатываемых этими
При
Типичная гипотеза при программировании работы с
Объект не изменен, если на него не было воздействий из программы.
Но реальность зачастую требует понимания и учета более сложных обстоятельств, что может существенно продлить время жизни программы или ее компонентов. В таком случае удобно предоставлять
Дальнейшее развитие подходов к декомпозиции программ связано с выделением отдельных проекций и шагов при решении сложных задач. Понятие проекции связано с различием точек зрения, позволяющим описывать решение всей задачи, но отражать в описании только видимые детали. По мере изменения точек зрения могут проступать новые детали, до тех пор, пока дальнейшая детализация не утрачивает смысл, т.е. улучшение трудно заметить или цена его слишком высока. Так, представление символьной информации в Лиспе выделено в отдельную проекцию, независимую от распределения памяти, вычисление значений четко отделено от компиляции программ, понятие связывания имен с их определениями и
Понятие шага обычно связывается с процессом раскрутки программ, оправданным в тех случаях, когда целостное решение задачи не может гарантировать получение приемлемого результата в нужный срок — это влечет за собой непредсказуемо большие трудозатраты.
Удобный подход к организации программ "отдельная работа отдельно программируется и отдельно выполняется" успешно показал себя при развитии операционной системы UNIX [21] как работоспособный принцип декомпозиции программ. Но существуют задачи, например
При
Выбор конкретных шагов можно соотнести с декомпозицией определения языка программирования на синтаксические и семантические, функциональные и машинно-ориентированные, языково-ориентированные и системные аспекты. При такой декомпозиции можно на первых шагах как бы "снять" синтаксическое и семантическое разнообразие языка, как имеющее чисто технический характер. Именно в этом смысл выделения элементарного Лиспа. Такая методика может быть успешна при освоении любого класса задач, информацию о котором можно представить в виде частично формализуемых текстовых и графовых форм.
Дальнейшие шаги раскрутки можно упорядочить по актуальности
При переходе от обычного стандартного программирования с
Чтобы сравнить дистанцию с функциональным программированием, рассмотрим самодельный встроенный в
При организации
Рассмотрим программу вычисления площадей ряда геометрических фигур, таких как круги, прямоугольники, звезды и пр. Ее запись в ОО-стиле поначалу может выглядеть непринципиально отличающейся от обычной программы.
Но допустим, понадобилось ввести новый класс — раскрашенные круги. Если
Практически
В
(gethash 'color obj)
Поскольку функции являются данными
(FUNCALLl (gethash 'move obj) obj 10)
Мы можем определить под эту идею синтаксис как в языке
(DEFUN tell (obj message rest args)
(APPLY (gethash messmage obj) obj args))
что позволяет сказать
(tell obj 'move 10)
Фактически успех GETHASH. Таким образом, определение данной функции нам сразу даст все три основные черты
Посмотрим, во что это обойдется в исходном примере.
Мы должны создать два
В
(SETF (gethash 'area circle-class)
#' (LAMBDA (x)
(* pi (expt (rget ' radius x) 2))))
Теперь можно спрашивать о площади круга, она будет вычисляться согласно rget при чтении tell — при вызове
(rget 'radius our-cicle)
(tell our-circle 'area)
Прежде чем улучшать эту программу, надо проверить, что же получилось. Легкость результата — это трюк, но не программистский трюк, а концептуальный. Не будем забывать о том, что
До сих пор речь шла о простом 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))))
Показанный в [7] пример работает по первому аргументу (выбор подходящего делает это на всех аргументах, причем с рядом вспомогательных средств, обеспечивающих гибкий перебор
(defclass ob () (f1 f2 ...))
Это означает, что каждое вхождение f1 f2 ... ( Слот — это поле записи или списка
(SETF с (make-instance 'ob))
Чтобы задать значение поля, используем специальную функцию:
(SETF (slot-value c) 1223)
До этого значения полей были не определены.
Простейшее определение слота — это его имя. Но в общем случае слот может содержать список
:allocation :class
Изменение такого слота будет доступно всем экземплярам
Нет необходимости все новые слоты создавать в каждом классе.
Пример: ОО-определение
;oop-compile
(defclass expr ()
((type :accessor td)
(sd :accessor ft))
(:documentation "C-expression"))
(defclass un (expr)
; \_____суперкласс для унарных форм
((type :accessor td) ;; можно унаследовать,
(sd :accessor ft)) ;; а здесь не дублировать
(:documentation "quote car *other *adr"))
(defclass bin (expr)
((type :accessor td)
(sd :accessor ft)
(sdd :accessor sd) )
(:documentation "cons + lambda let"))
(defclass trio (expr)
;; (bin) тогда можно не объявлять первые 3 поля
((type :accessor td)
(sd :accessor ft)
(sdd :accessor sd)
(sddd :accessor td) )
(:documentation "if label"))
(defmethod texrp ((x expr) (nt atom))
(SETF (slot-value x 'type) nt)
(SETF (td x) nt) ;;--;; variant
(:documentation "объявляем тип выражения"))
(defmethod spread ((hd (eql 'QUOTE))
(tl expr))
(let ( (x (make-instance 'un)) )
(SETF (ft x) (car tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'QUOTE))
(tl expr))
(list 'LDC tl)
(:documentation "сборка кода"))
(defmethod compl ((hd (eql 'CAR))
(tl expr)) N)
(append (compl(ft tl) N) '(CAR))
(:documentation "сборка кода"))
(defmethod spread ((hd (eql 'CONS))
(tl expr))
(let ( (x (make-instance 'bin)) )
(SETF (ft x) ( CAR tl))
(SETF (sd x) ( cadr tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'CONS))
(tl bin) N )
(append (compl(sd tl) N) (compl(ft tl) N)
'(CONS))
(:documentation "сборка кода"))
(defmethod compl ((hd (eql '+))
(tl bin) N )
(append (compl(ft tl) N) (compl(sd tl) N)
'(ADD))
(:documentation "сборка кода"))
(defmethod spread ((hd (eql 'IF))
(tl expr))
(let ( (x (make-instance 'trio)) )
(SETF (ft x) ( CAR tl))
(SETF (sd x) ( cadr tl))
(SETF (td x) ( caddr tl))
(SETF (td x) hd)
) (:documentation "распаковка выражения"))
(defmethod compl ((hd (eql 'IF))
(tl expr) N )
(let ( (then (list (compl(sd tl) N) '(JOIN)))
(else (list (compl(td tl) N) '(JOIN))) )
(append (compl(ft tl) N) (list 'SEL then else)
)
)(:documentation "сборка кода"))
(defmethod parh ((x expt))
(let (ftx (ft x))
(COND
((ATOM ftx) (spread 'ADR ftx))
((member (CAR ftx)
'(QUOTE CAR CONS + IF LAMBDA LABEL LET))
(spread (CAR ftx) (CDR ftx))
(T (spread 'OTHER ftx) ))
)(:documentation "шаг разбора"))
;====test==========
(SETF test1 (make-instance 'expr))
(texpr test1 'expr)
(SETF (slot-value test1 'sd) (READ))
()
(SETF e1 (make-instance 'expr))
(SETF e2 (make-instance 'expr))
(SETF e3 (make-instance 'expr))
(print (tf e2))
(SETF (slot-value e3 'type) 'expr)
(print (tf e3))
(SETF (slot-value e3 'sd) '(QUOTE const))
(defmethod ep ((x expr))
((LAMBDA (xt)
(SETF (slot-value x 'type) xt) )
(CAR (slot-value x 'sd)) ) )
(print (ep e3))
(print (tf e3))
(print (td e3))
(print (sd e3))
(defmethod ep-q ((x (eql 'QUOTE)) (y expr))
(SETF y (make-instance 'un)))
(SETF (slot-value y 'type) 'QUOTE)
(SETF (slot-value y 'sd) y)
(print (tf (e3 'sd)))
(print (tf e1))
(print(SETF (slot-value e1 'type) (tf e1)))
(SETF (slot-value e2 'sd) 'atom1)
(print (tf (sd e2)))
(print(SETF (slot-value e3 'sd) '(QUOTE const)))
(print (tf e3))
, естественно, использует модель
Более интересный вопрос, что же нам еще может дать функциональный стиль и лисповская традиция
Ответу на этот вопрос посвящены три следующие лекции.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.