Моделирование
Традиционно система программирования для языка
NIL , являющийся реализацией пустого списка () ;LABEL ;SUBR );CONS ;CAR, CDR , ATOM , EQ, представляющими эти операции;QUOTE, COND и LAMBDA, соответственно;EVAL ;Такое
При ассоциировании атомов с произвольной информацией можно использовать специально организованный
((T . T )(NIL . NIL))
обеспечивает значение T и в соответствии с семантикой базового
((ОДИН . 1)(ДВА . 2))
дает символьные имена числовым значениям, а список
((ГОЛОВА . CAR)(ХВОСТ . CDR)(УЗЕЛ . CONS))
— синонимы для обозначения базовых операций
Основой определения интерпретатора является функция EVAL (AL. Спецификация такой функции для базового
(EVAL NIL AL ) ; = NIL
(EVAL T AL ) ; = T
(EVAL 'X AL ) ; = (CADR (ASSOC X AL))
(EVAL '(QOUTE EXPR ) AL ) ; = EXPR
(EVAL '(COND ((T YES) ... )) AL ) ; = YES
(EVAL '(SETQ X Y ) AL )
; = (CDAR(CONS (CONS 'X (EVAL Y )) AL))
(EVAL '(CAR A) '((A 1 2 3)(NIL NIL)) ) ; = 1
В других случаях выражения получают значение в результате применения некоторой функции, стоящей в APPLY. Для ее работы необходима функция, вычисляющая значения аргументов с учетом состояния ассоциативного списка.
При написании на базовом Лиспе определения функции EVAL согласно приведенной выше спецификации, способной от данного списочного представления выражения E перейти к его значению с учетом заданного ассоциативного списка AL, хранящего значения атомов, мы несколько отступаем от ранее данных определений, с тем чтобы более явно выделить линии сборки системы.
(DEFUN EVAL (e al )
(COND
((MEMBER e '(NIL T )) e )
((ATOM e ) ((LAMBDA (v )
(COND (v (CADR v ) )
(T (ERROR ''undefined_value ))
))
(ASSOC e al )
) )
((EQ (CAR e) 'QUOTE ) (CAR (CDR e )) )
((EQ (CAR e) 'COND ) (EVCON (CDR e ) al ) )
((EQ (CAR e) 'LET )
(EVAL (CADDDR e )
(CONS (CONS (CADR e )
(CONS (EVAL (CADDR e ) al ) NIL) ) al )
))
(T (APPLY (CAR e) (EVLIS (CDR e) al ) al ) )
))
В этом функциональном значении используется имя функции APPLY, применяющей произвольную функцию к ее аргументам при заданном ассоциативном списке. ERROR —
Определение функции APPLY работает при условии, что функция SUBR осуществляет применение встроенных функций к их аргументам, заданным в виде списка значений.
(DEFUN APPLY (fn args al )
(COND
((MEMBER fn '(CAR CDR CONS ATOM EQ ))
(SUBR (CADR (ASSOC fn al)) args ))
((EQ fn NIL) NIL)
((ATOM fn ) (APPLY (EVAL fn al ) args al ))
((EQ (CAR fn ) 'LAMBDA )
(EVAL (CADDR fn )
(APPEND (PAIR (CADR fn ) args ) al )
) )
((EQ (CAR fn) 'LABEL )
(APPLY (CADDR fn) args
(CONS (CDR fn ) al )
) )
(T (ERROR- 'undefined_function))
) )
Обратите внимание, что в EVAL при поиске атома в ассоциативном списке мы допускаем отсутствие ассоциированного с атомом значения и сообщаем об этом диагностикой с помощью функции ERROR. В APPLY же при выборе адреса встроенной функции мы рассчитываем, что все известные функции реализованы, и их адреса размещены в ассоциативном списке — за правильность выбора имен встроенных функций отвечает программист.
Можно еще поработать с таким определением интерпретатора и более четко локализовать его зависимость от четырех различных категорий объектов: самоопределяемые атомы — (, базовые операции над данными языка, обрабатывающие предварительно вычисленные аргументы, — (CAR , специальные функции, управляющие обработкой аргументов без их предварительного вычисления, — (QUOTE и конструкторы функций, строящие функциональные значения из обычных выражений, — (LAMBDA LABEL... ).
Желающие могут поэкспериментировать с самодельным интерпретатором, превращая его в модель ядра любого языка программирования, используя какую-нибудь
Упражнение 9.1. Пусть READ и PRINT — встроенные функции, обеспечивающие прием с клавиатуры и вывод на экран произвольных данных языка
Ответ.
(DEFINE CIRCLE (al ) (PRINT (EVAL (PRINT (READ ))
al ))
(CIRCLE al ) )
"Но оно же зациклится!" — скажете вы и будете правы.
Но это не помешает эксперименту, ведь в нашем распоряжении имеется конец файла Ctrl-Z или встроенная операция завершения процесса типа BYE, EXEC, SYSTEM либо что-то подобное.
Упражнение 9.2. Полученные 50 строк определения
После обсуждения схемы функционирования
Обычно при обработке программ в памяти располагаются разносортные результаты, возникающие при разборе и анализе текста программы и ее данных, при построении ее внутреннего кода и при формировании результата. В
Кроме того, почти исключается необходимость присваиваний, они в программах заменяются именованием.
Память обычно распределена по блокам, содержащим ряд слов, образующих структуры данных. Физический объем памяти, логическая длина данных и состав информации, полезной для продолжения вычислений, могут существенно различаться. Минимальные потери в результативности работы с памятью дает динамическая обработка бинарных деревьев — нет простоев из-за незаполненности части полей. Каждый узел такого дерева имеет небольшой объем, достаточный для хранения двух CAR и , левый и правый). Типизация указателей нужна для динамического контроля соответствия данных и операций по их обработке. , атомы, списки, числа, строки — все это реализационно различимые типы данных. (Утверждение о бестиповости
Реализация бинарных деревьев или LET, LABELS в системе PUT задает индикатор и соответствующее ему новое значение свойства атома, а функция GET обеспечивает доступ к свойству атома, соответствующему заданному индикатору. Теперь с помощью списков свойств мы могли бы добиться точного соответствия семантики констант и определений функций их спецификации в базовом Лиспе, но не будем отвлекаться на это.
Самым интересным, можно сказать революционным, механизмом работы с памятью в Лиспе, бесспорно, стала "сборка мусора". С начала 1960-х годов методам такой работы посвящены многочисленные исследования, которые продолжаются до наших дней и сильно активизировались в связи с включением похожего механизма в реализацию языка Java.
Общая идея всех таких методов достаточно проста:
Специальная программа "Сборщик мусора" выполняет анализ достижимости всех блоков памяти просто пометкой узлов, видимых из конечного числа рабочих регистров системы программирования. К таким регистрам относятся промежуточные результаты вычислений, активная часть стека, . Такая автоматизация не лишена недостатков, но они обнаруживаются лишь в сравнительно сложных процессах, требования которых мы сейчас не учитываем.
Обычно с машиной связывается представление о блоках информации фиксированного объема, таких как слова, байты, регистры. Функциональное программирование нацелено на более крупные построения — структуры данных не ограниченной заранее длины. Такие структуры достаточно эффективно реализуются посредством специального стека, приспособленного к обработке произвольного числа компонентов текущего уровня иерархической структуры данных.
От обычного стека он отличается выделением указателя на конец текущего уровня.
При переходе на новый внутренний уровень Кон_тек_ур записывается в стек, Нач_стека переписывается в Кон_тек_ур, а адрес новой
В результате стек хранит ссылки на
Значительный резерв производительности функциональных программ дают
| APPEND | NCONC |
| SUBST | NSUBST |
| REMOVE | DELETE |
| REVERSE | NREVERSE |
| UNION | NUNION |
Реальный состав системы и возможности ее компонентов можно исследовать с помощью специальных функций, предоставляющих информацию о включенных в систему объектах и их свойствах.
Состав системы:
( — печатает информацию обо всех символах, имена которых содержат подстроку " nm ". Второй аргумент, если он указан, ограничивает эту информации заданным пакетом.
( — дает описание места объекта в системе.
(SYMBOL-PLIST 'fn) — выдает перечень всех свойств объекта.
(DOCUMENTATION 'fn 'function) — выдает документацию по объекту.
Отладка программ:
(DRIBBLE 'file) — направляет в
(STEP expr) — обеспечивает пошаговый режим интерпретации выражения с выдачей результатов каждого шага.
Ввод-вывод данных:
(SETQ inpt (OPEN file-in :direction :input )) — заведение переменной для обозначения открытого
(READ inpt) — чтение из
(PRINT (PRINT eval-val prtcl) outpt) — запись данного eval-val в два разных
(OPEN file-in :direction :input ) — открытие
Далее следуют три варианта открытия
(OPEN "output" :direction :output :if-exists
:rename :if-does-not-exist :create)
(OPEN "protocol" :direction :output :if-exists
:overwrite :if-does-not-exist :create)
(OPEN "history" :direction :output :if-exists
:append :if-does-not-exist :create )
(CLOSE prtcl) — закрытие потока.
Особенности работы с
(DEFUN eval-protocol () (PROG (eval-val)
; выражения хранятся в файле "input.lsp"
metka
(PRINT '> prtcl)
(SETQ eval-val (EVAL
(list 'STEP ; пошаговое вычисление выражения
(PRINT (PRINT
( if (eq 'eof (setq rinpt
(READ inpt NIL 'eof) ))
(return(CLOSE inpt))
rinpt)
prtcl) hstry)
)))
; прочитанное записывается в файлы "protocol" и
; "history"
(PRINT '- prtcl)
;(print eval-val)
(print (print eval-val
prtcl) outpt)
; результат интерпретации в файлах
; "protocol" и "output"
(go metka)
))
(DEFUN help ( function-name )
(ed (string function-name )) )
(DEFUN step1 (file-in)
(PROG ()
(SETQ inpt (OPEN file-in :direction :input ))
(SETQ outpt (OPEN "output" :direction :output
:if-exists :rename
:if-does-not-exist :create))
(SETQ prtcl (OPEN "protocol" :direction :output
:if-exists :overwrite
:if-does-not-exist :create))
(SETQ hstry (OPEN "history" :direction :output
:if-exists :append
:if-does-not-exist :create ))
(PRINT '"****** new-session ******" hstry)
; цикл прервется по достижении конца файла ввода
(eval-protocol)
(CLOSE prtcl)
(CLOSE hstry)
(CLOSE outpt)
))
(step1 "input.lsp")
; интерпретируемые выражения хранятся в файле
; "input.lsp"
Моделирование
Традиционно система программирования для языка
NIL , являющийся реализацией пустого списка () ;LABEL ;SUBR );CONS ;CAR, CDR , ATOM , EQ, представляющими эти операции;QUOTE, COND и LAMBDA, соответственно;EVAL ;Такое
При ассоциировании атомов с произвольной информацией можно использовать специально организованный
((T . T )(NIL . NIL))
обеспечивает значение T и в соответствии с семантикой базового
((ОДИН . 1)(ДВА . 2))
дает символьные имена числовым значениям, а список
((ГОЛОВА . CAR)(ХВОСТ . CDR)(УЗЕЛ . CONS))
— синонимы для обозначения базовых операций
Основой определения интерпретатора является функция EVAL (AL. Спецификация такой функции для базового
(EVAL NIL AL ) ; = NIL
(EVAL T AL ) ; = T
(EVAL 'X AL ) ; = (CADR (ASSOC X AL))
(EVAL '(QOUTE EXPR ) AL ) ; = EXPR
(EVAL '(COND ((T YES) ... )) AL ) ; = YES
(EVAL '(SETQ X Y ) AL )
; = (CDAR(CONS (CONS 'X (EVAL Y )) AL))
(EVAL '(CAR A) '((A 1 2 3)(NIL NIL)) ) ; = 1
В других случаях выражения получают значение в результате применения некоторой функции, стоящей в APPLY. Для ее работы необходима функция, вычисляющая значения аргументов с учетом состояния ассоциативного списка.
При написании на базовом Лиспе определения функции EVAL согласно приведенной выше спецификации, способной от данного списочного представления выражения E перейти к его значению с учетом заданного ассоциативного списка AL, хранящего значения атомов, мы несколько отступаем от ранее данных определений, с тем чтобы более явно выделить линии сборки системы.
(DEFUN EVAL (e al )
(COND
((MEMBER e '(NIL T )) e )
((ATOM e ) ((LAMBDA (v )
(COND (v (CADR v ) )
(T (ERROR ''undefined_value ))
))
(ASSOC e al )
) )
((EQ (CAR e) 'QUOTE ) (CAR (CDR e )) )
((EQ (CAR e) 'COND ) (EVCON (CDR e ) al ) )
((EQ (CAR e) 'LET )
(EVAL (CADDDR e )
(CONS (CONS (CADR e )
(CONS (EVAL (CADDR e ) al ) NIL) ) al )
))
(T (APPLY (CAR e) (EVLIS (CDR e) al ) al ) )
))
В этом функциональном значении используется имя функции APPLY, применяющей произвольную функцию к ее аргументам при заданном ассоциативном списке. ERROR —
Определение функции APPLY работает при условии, что функция SUBR осуществляет применение встроенных функций к их аргументам, заданным в виде списка значений.
(DEFUN APPLY (fn args al )
(COND
((MEMBER fn '(CAR CDR CONS ATOM EQ ))
(SUBR (CADR (ASSOC fn al)) args ))
((EQ fn NIL) NIL)
((ATOM fn ) (APPLY (EVAL fn al ) args al ))
((EQ (CAR fn ) 'LAMBDA )
(EVAL (CADDR fn )
(APPEND (PAIR (CADR fn ) args ) al )
) )
((EQ (CAR fn) 'LABEL )
(APPLY (CADDR fn) args
(CONS (CDR fn ) al )
) )
(T (ERROR- 'undefined_function))
) )
Обратите внимание, что в EVAL при поиске атома в ассоциативном списке мы допускаем отсутствие ассоциированного с атомом значения и сообщаем об этом диагностикой с помощью функции ERROR. В APPLY же при выборе адреса встроенной функции мы рассчитываем, что все известные функции реализованы, и их адреса размещены в ассоциативном списке — за правильность выбора имен встроенных функций отвечает программист.
Можно еще поработать с таким определением интерпретатора и более четко локализовать его зависимость от четырех различных категорий объектов: самоопределяемые атомы — (, базовые операции над данными языка, обрабатывающие предварительно вычисленные аргументы, — (CAR , специальные функции, управляющие обработкой аргументов без их предварительного вычисления, — (QUOTE и конструкторы функций, строящие функциональные значения из обычных выражений, — (LAMBDA LABEL... ).
Желающие могут поэкспериментировать с самодельным интерпретатором, превращая его в модель ядра любого языка программирования, используя какую-нибудь
Упражнение 9.1. Пусть READ и PRINT — встроенные функции, обеспечивающие прием с клавиатуры и вывод на экран произвольных данных языка
Ответ.
(DEFINE CIRCLE (al ) (PRINT (EVAL (PRINT (READ ))
al ))
(CIRCLE al ) )
"Но оно же зациклится!" — скажете вы и будете правы.
Но это не помешает эксперименту, ведь в нашем распоряжении имеется конец файла Ctrl-Z или встроенная операция завершения процесса типа BYE, EXEC, SYSTEM либо что-то подобное.
Упражнение 9.2. Полученные 50 строк определения
После обсуждения схемы функционирования
Обычно при обработке программ в памяти располагаются разносортные результаты, возникающие при разборе и анализе текста программы и ее данных, при построении ее внутреннего кода и при формировании результата. В
Кроме того, почти исключается необходимость присваиваний, они в программах заменяются именованием.
Память обычно распределена по блокам, содержащим ряд слов, образующих структуры данных. Физический объем памяти, логическая длина данных и состав информации, полезной для продолжения вычислений, могут существенно различаться. Минимальные потери в результативности работы с памятью дает динамическая обработка бинарных деревьев — нет простоев из-за незаполненности части полей. Каждый узел такого дерева имеет небольшой объем, достаточный для хранения двух CAR и , левый и правый). Типизация указателей нужна для динамического контроля соответствия данных и операций по их обработке. , атомы, списки, числа, строки — все это реализационно различимые типы данных. (Утверждение о бестиповости
Реализация бинарных деревьев или LET, LABELS в системе PUT задает индикатор и соответствующее ему новое значение свойства атома, а функция GET обеспечивает доступ к свойству атома, соответствующему заданному индикатору. Теперь с помощью списков свойств мы могли бы добиться точного соответствия семантики констант и определений функций их спецификации в базовом Лиспе, но не будем отвлекаться на это.
Самым интересным, можно сказать революционным, механизмом работы с памятью в Лиспе, бесспорно, стала "сборка мусора". С начала 1960-х годов методам такой работы посвящены многочисленные исследования, которые продолжаются до наших дней и сильно активизировались в связи с включением похожего механизма в реализацию языка Java.
Общая идея всех таких методов достаточно проста:
Специальная программа "Сборщик мусора" выполняет анализ достижимости всех блоков памяти просто пометкой узлов, видимых из конечного числа рабочих регистров системы программирования. К таким регистрам относятся промежуточные результаты вычислений, активная часть стека, . Такая автоматизация не лишена недостатков, но они обнаруживаются лишь в сравнительно сложных процессах, требования которых мы сейчас не учитываем.
Обычно с машиной связывается представление о блоках информации фиксированного объема, таких как слова, байты, регистры. Функциональное программирование нацелено на более крупные построения — структуры данных не ограниченной заранее длины. Такие структуры достаточно эффективно реализуются посредством специального стека, приспособленного к обработке произвольного числа компонентов текущего уровня иерархической структуры данных.
От обычного стека он отличается выделением указателя на конец текущего уровня.
При переходе на новый внутренний уровень Кон_тек_ур записывается в стек, Нач_стека переписывается в Кон_тек_ур, а адрес новой
В результате стек хранит ссылки на
Значительный резерв производительности функциональных программ дают
| APPEND | NCONC |
| SUBST | NSUBST |
| REMOVE | DELETE |
| REVERSE | NREVERSE |
| UNION | NUNION |
Реальный состав системы и возможности ее компонентов можно исследовать с помощью специальных функций, предоставляющих информацию о включенных в систему объектах и их свойствах.
Состав системы:
( — печатает информацию обо всех символах, имена которых содержат подстроку " nm ". Второй аргумент, если он указан, ограничивает эту информации заданным пакетом.
( — дает описание места объекта в системе.
(SYMBOL-PLIST 'fn) — выдает перечень всех свойств объекта.
(DOCUMENTATION 'fn 'function) — выдает документацию по объекту.
Отладка программ:
(DRIBBLE 'file) — направляет в
(STEP expr) — обеспечивает пошаговый режим интерпретации выражения с выдачей результатов каждого шага.
Ввод-вывод данных:
(SETQ inpt (OPEN file-in :direction :input )) — заведение переменной для обозначения открытого
(READ inpt) — чтение из
(PRINT (PRINT eval-val prtcl) outpt) — запись данного eval-val в два разных
(OPEN file-in :direction :input ) — открытие
Далее следуют три варианта открытия
(OPEN "output" :direction :output :if-exists
:rename :if-does-not-exist :create)
(OPEN "protocol" :direction :output :if-exists
:overwrite :if-does-not-exist :create)
(OPEN "history" :direction :output :if-exists
:append :if-does-not-exist :create )
(CLOSE prtcl) — закрытие потока.
Особенности работы с
(DEFUN eval-protocol () (PROG (eval-val)
; выражения хранятся в файле "input.lsp"
metka
(PRINT '> prtcl)
(SETQ eval-val (EVAL
(list 'STEP ; пошаговое вычисление выражения
(PRINT (PRINT
( if (eq 'eof (setq rinpt
(READ inpt NIL 'eof) ))
(return(CLOSE inpt))
rinpt)
prtcl) hstry)
)))
; прочитанное записывается в файлы "protocol" и
; "history"
(PRINT '- prtcl)
;(print eval-val)
(print (print eval-val
prtcl) outpt)
; результат интерпретации в файлах
; "protocol" и "output"
(go metka)
))
(DEFUN help ( function-name )
(ed (string function-name )) )
(DEFUN step1 (file-in)
(PROG ()
(SETQ inpt (OPEN file-in :direction :input ))
(SETQ outpt (OPEN "output" :direction :output
:if-exists :rename
:if-does-not-exist :create))
(SETQ prtcl (OPEN "protocol" :direction :output
:if-exists :overwrite
:if-does-not-exist :create))
(SETQ hstry (OPEN "history" :direction :output
:if-exists :append
:if-does-not-exist :create ))
(PRINT '"****** new-session ******" hstry)
; цикл прервется по достижении конца файла ввода
(eval-protocol)
(CLOSE prtcl)
(CLOSE hstry)
(CLOSE outpt)
))
(step1 "input.lsp")
; интерпретируемые выражения хранятся в файле
; "input.lsp"
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.