Эта глава предназначена для реализационного уточнения уже известных теоретических
рассуждений. Ряд уточнений показан на примере, представляющем
Функции
member = lambda [a;x]
[ null[x] >> Nil ]
[ eq[a;car[x]] >> T ]
[ T >> member[a;cdr[x]] ]
union = lambda[x;y]
[ null[x] >> y ]
[ member[car[x];y] >> union[cdr[x];y] ]
[ T >> cons[car[x];union[cdr[x];y]] ]
intersection = lambda [x;y]
[ null[x] >> NIL ]
[ member[car[x];y] >>
cons[car[x];intersection[cdr[x];y]] ]
[ T >> intersection[cdr[x];y] ]
Определяя эти функции на
(DEFUN MEMBER (A X)
(COND
((NULL X) Nil)
((EQ A (CAR X)) T)
(T (MEMBER A (CDR X)) )
) )
(DEFUN UNION (X Y)
(COND
((NULL X) Y)
((MEMBER (CAR X) Y) (UNION (CDR X) Y) )
(T (CONS (CAR X) (UNION (CDR X) Y))) )) )
) )
(DEFUN INTERSECTION (X Y)
(COND
((NULL X) NIL)
((MEMBER (CAR X) Y)
(CONS (CAR X) (INTERSECTION (CDR X) Y)) )
(T (INTERSECTION (CDR X) Y))
) )
(INTERSECTION '(A1 A2 A3) '(A1 A3 A5))
(UNION '(X Y Z) '(U V W X))
Эта
Первые три формы сводятся к применению
Значение четвертой формы — (A1 A3). Значение пятой формы — (Y Z C B D X). Анализ пути, по которому выполняется рекурсия, показывает, почему элементы множества появляются именно в таком порядке.
В этом примере продемонстрировано несколько элементарных правил написания функциональных
((A . B) X (C . (E F D)))— допустимое S-выражение. Оно может быть записано как
((A . B) . ( X . ((C . (E . ( F . (D . Nil)))) . Nil)))или
((A . B) X (C E F D))
(A B C . D) есть сокращение для (A . ( B . ( C . D) )). Любая другая
расстановка точек на одном уровне есть ошибка, например (A. B C).Вывод S-выражений на печать и в файлы выполняет
(LOAD 'TEST.LST) (PRINT (INTERSECTION '(A1 A2 A3) '(Al A3 A5)) ) (PRINT (UNION '(X Y Z) '(U V W X)) ) (PRINT (UNION (READ) '(1 2 3 4)) ) ; объединение вводимого списка со списком ; '(1 2 3 4)
Таким образом, возвращаясь к Дж.Маккарти [1]:
"Можно написать "a + b, где a = 341 и b = 216". В такой ситуации не может быть недоразумений, и все согласятся, что ответ есть 557. Чтобы получить этот результат, необходимо заменить
переменные фактическими значениями, и затем сложить два числа (на арифмометре, например).
Одна из причин, по которой в этом случае не возникает недоразумений, состоит в том, что "a" и "b" не есть приемлемые входы для арифмометра, и следовательно, очевидно, что они только представляют
фактические аргументы . Присимвольных вычислениях ситуация может быть более сложной. Атом может быть какпеременной , так ифактическим аргументом . К дальнейшему усложнению приводит то обстоятельство, что некоторые из аргументов могут бытьпеременными , вычисляемыми внутри вызова другой функции. В таких случаях интуитивный подход может подвести. Для эффективного программирования в функциональном стиле необходимо более точное пониманиеформализмов .
Чтобы не обескураживать читателей, следует заметить, что здесь ничего принципиально нового нет. Все, что сейчас рассматривается, может быть логически выведено из правил представления
программ в виде S-выражений или из определенияуниверсальных функций eval/apply, и является их непосредственным следствием, возможно, не вполне очевидным.
Любой формализм для
переменных сводится к лямбда-обозначению. Часть интерпретатора, которая при вычислении функций связываетпеременные , называется APPLY. Когда APPLY встречает функцию, начинающуюся с LAMBDA, списокпеременных попарно связывается со списком аргументов и добавляется к началу а-списка. При вычислении функции могут быть обнаруженыпеременные . Они вычисляются поиском в а-списке. Еслипеременная встречается несколько раз, то используется последнее или самое новое значение. Часть интерпретатора, которая делает это, называется EVAL. Проиллюстрируем данное рассуждение на примере. Предположим, что интерпретатор получает следующее S-выражение:
((LAMBDA (X Y) (CONS X Y)) 'A 'B)
Функция:
((LAMBDA (X Y) (CONS X Y)) 'A 'B)
Аргументы:
(A B)
EVAL через EVAL-A передает эти аргументы функции APPLY. (См. лек. 3).
(APPLY #'(LAMBDA (X Y) (CONS X Y)) '(A B) Nil )
APPLY свяжет
переменные и передаст функцию и удлинившийся а-список EVAL для вычисления.
(EVAL '(CONS X Y) ' ((X . A) (Y . B) . Nil))
EVAL вычисляет
переменные и сразу передает ихконсолидации - функцииCONS , строящей из них бинарный узел.
(Cons 'A 'B) = (A . B)
EVAL вычисляет
переменные и сразу передает ихконсолидации , строящей из них бинарный узел.
Реальный интерпретатор пропускает один шаг, требуемый формальным определением
универсальных функций ".
На практике сложилась традиция включать в
(DEFUN UNION (x y)
(LET ( (a-x (CAR x))
(d-x (CDR x))
) ; конец списка локальных именованных значений
(COND ((NULL x) y)
((MEMBER a-x y) (UNION d-x y) ) ; использование локальных
(T (CONS a-x (UNION d-x y)) ) ; значений из контекста
) ) ; завершение контекста LET
)
(DEFUN MEMBER (a x)
(LET* ( (N-X (NULL x))
(a-x (CAR x))
(d-x (CDR x))
(e-car (EQ a a-x))
) ; список локально именованных выражений
(COND (N-X Nil) ; использование
(E-CAR T) ; именованных
(T (MEMBER A D-X)) ; выражений
) ) ; выход из контекста именованных выражений
)
(Эквивалентность с точностью до побочного эффекта.)
Глобальные
(DEFPARAMETER GLOB '(a b c))
Значение такой
(LET ((GLOB 12))(PRINT GLOB)) (PRINT GLOB)
напечатано будет:
12 (A B C)
Дж.Маккарти обращает внимание [1]:"Иногда говорят, что
Обычно (a . v) в конец a-списка. Но в реальных
(DefConstant X '(A B C D))
Особый интерес представляет тип
Ситуация, когда атом обозначает функцию, реализационно подобна той, в которой
атом обозначает аргумент. Если функция рекурсивна, то ей надо дать имя. Теоретически это
делается с помощью формы LABEL, которая связывает название с определением функции
в ассоциативном списке (а-списке). Название связано с определением функции точно
так же, как
Тот факт, что большинство функций —
(LABELS ( (INTERSECTION (x y)
(LET* ( (N-X (NULL x))
(MEM-CAR (MEMBER (CAR x) y))
(INT #'INTERSECTION)
) ; конец списка локальных выражений let*
(FLET ((F-TAIL (FN sx sy)
(APPLY FN (LIST (CDR sx) sy)) )
(CONS-F-TAIL (FN sx sy)
(CONS (CAR sx)
(APPLY FN (LIST (CDR sx) sy))
)) ) ; конец списка нерекурсивных функций FLET
(COND (N-X NIL) ; выражение, использующее
(MEM-CAR (cons-f-tail INT x y) ) ; локальные определения функций
(T (f-tail INT x y)) ) ; из контекстов FLET и
; LABELS
) ; выход из контекста FLET
) ; выход из контекста LET*
) ; завершено определение INTERSECTION
) ; конец списка локальных рекурсивных функций
(DEFUN UNION (x y)
(LET ( (a-x (CAR x))
(d-x (CDR x))
)
(COND ((NULL x) y)
((MEMBER a-x y) (UNION d-x y) )
(T (CONS a-x (UNION d-x y)) )
) ) ) ; завершено определение на текущем уровне
(INTERSECTION '(A1 A2 A3) '(A1 A3 A5))
(UNION '(X Y Z) '(U V W X))
) ; выход из контекста LABELS
Некоторые функции вместо определений с помощью S-выражений закодированы
как замкнутые машинные
Обычно EVAL вычисляет аргументы функций до применения к ним функций с помощью
APPLY. Таким образом, если EVAL задано (, то сначала вычисляются X и Y,
а потом над полученными значениями работает (QUOTE X),
то X не будет вычисляться. QUOTE —
Возможность использования безымянных определений функций не вполне очевидна
для вспомогательных рекурсивных функций, так как их имена нужны в их собственных
определениях. Но при необходимости и определение рекурсивной функции можно привести
к форме, не зависящей от ее имени. Такие имена формально работают как связанные
Пример (предложен В.А. Потапенко).
Преобразуем определение факториала в самоприменимую безымянную форму.
Для этого нужно:
Традиционное определение факториала:
(DEFUN N! (n)
(COND
((EQ n 0) 1)
(T (* n (N! (- n 1)))) ; Факториал
; Выход из рекурсии
; Рекурсивный виток с редукцией аргумента
) ) ; и умножением на результат предыдущего витка
Строим самоприменимое определение факториала:
(DEFUN N!_self (f n) ; Обобщенная функция,
; равносильная факториалу при f = N!_self
(COND ((EQ n 0)1)
(T (* n (APPLY f (list f (- n 1)))))
; применение функции f
; к списку из уменьшенного аргумента
) )
Использовать это определение можно следующим образом:
(N!_self #'N!_self 3) ; = 6 =
или
(APPLY 'N!_self '(N!_self 4)) ; = 24 =
При таких аргументах оно эквивалентно исходному определению факториала. Теперь избавимся от названия функции:
((LAMBDA (f n )
; безымянная функция, равносильная факториалу
; при f = N!_self
(COND ((EQ n 0)1)
(T (* n ( f (list f (- n 1)))))
))
Использовать это определение можно в следующей форме:
((LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) )) ; функция
(LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) )) ;первый аргумент — f
5 ; второй аргумент — n
) ; = 120 - результат самоприменения факториала
или
(APPLY
#'(LAMBDA (f n )
(COND ((EQ n 0)1)(T (* n (apply f (list f
(- n 1))))) ) ) ; функция
'((LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) ))
6 ) ; список аргументов
)) ; = 720
Можно записать этот текст программы (код) без дублирования определения функции:
(LAMBDA (n)
( (LAMBDA (f) (APPLY f (list f n))))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(APPLY f (list f (- n 1)))) ) )
; внутренняя функция f
) ))
И использовать полученное определение следующим образом:
((LAMBDA (n)
((LAMBDA (f) (APPLY f (list f n)))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(APPLY f (list f (- n 1)))))
) ) )) 5 ) ; = 120 )
Сокращаем совпадающие подфункции и получаем форму, в которой основные содержательные компоненты локализованы:
((LAMBDA (n) (flet ((afl (f n)
(apply f (list f n)) ))
((LAMBDA (f) (afl f n))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(afl f (- n 1))))
)))))
6 ) ; = 720
)
Таким образом, определение рекурсивной функции можно преобразовать к
безымянной форме. Техника
Такое, пусть не самое понятное, определение позволяет получить больше, чем просто скорость исполнения кода и его переносимость. Техника функциональных определений и их преобразований позволяет рассматривать решение задачи с той точки зрения, с какой это удобно при постановке задачи, с естественной степенью подробности, гибкости и мобильности.
(FUNCALL F a1 a2 ... ) = (APPLY F (list a1 a2 ...))
Разрастание числа функций, манипулирующих применением функций в языке
Цель этой части — помочь избежать некоторых общих ошибок при отладке программ.
(CAR '(A B)) = (CAR (QUOTE(A B))
Функция: CAR
Аргументы: ((A B))
Значение есть A. Заметим, что интерпретатор ожидает (A B). Добавочная пара скобок возникает,
т.к. APPLY подается
Можно написать (LAMBDA(X)(CAR X)) вместо просто CAR. Это корректно, но
не является необходимым.
(CONS 'A '(B . C))
Функция:
Аргументы: (A (B . C))
Результат (A . (B . C)) (A B . C)
((CAR (QUOTE (A . B))) CDR (QUOTE (C . D)))
Функция:
Аргументы: ((CAR (QUOTE (A . B))) (
Значением такого вычисления будет
((CAR (QUOTE (A . B))) . (CDR (QUOTE (C . D))))
Скорее всего, это совсем не то, чего ожидал новичок. Он рассчитывал вместо (CAR (QUOTE (A . B)) получить A и увидеть (A . D) в качестве итогового значения
(CONS (CAR (QUOTE (A . B))) (CDR (QUOTE (C . D))) )
ниже приведены еще три правильных способа записи нужной формы.
Первый состоит в том, что CAR и
((LAMBDA (X Y) (CONS (CAR X) (CDR Y))) '(A . B) '(C . D))
Функция: (LAMBDA (X Y) (
Аргументы: ((A . B)(C . D))
(EVAL '(CONS (CAR (QUOTE (A . B)))
(CDR (QUOTE (C . D)))) Nil)
Функция: EVAL
Аргументы: ((
Значением того и другого является (A . D)
((LAMBDA (X Y) (CONS (EVAL X) (EVAL Y))) '(CAR (QUOTE (A . B)))' (CDR (QUOTE (C . D))) )
Функция: (LAMBDA (X Y) (
Аргументы: ((CAR (QUOTE (A . B))) (
Решения этого примера показывают, что грань между функциями и данными достаточно условна — одни и те же вычисления можно осуществить при разном распределении промежуточных вычислений внутри выражения, передвигая эту грань.
Эта глава предназначена для реализационного уточнения уже известных теоретических
рассуждений. Ряд уточнений показан на примере, представляющем
Функции
member = lambda [a;x]
[ null[x] >> Nil ]
[ eq[a;car[x]] >> T ]
[ T >> member[a;cdr[x]] ]
union = lambda[x;y]
[ null[x] >> y ]
[ member[car[x];y] >> union[cdr[x];y] ]
[ T >> cons[car[x];union[cdr[x];y]] ]
intersection = lambda [x;y]
[ null[x] >> NIL ]
[ member[car[x];y] >>
cons[car[x];intersection[cdr[x];y]] ]
[ T >> intersection[cdr[x];y] ]
Определяя эти функции на
(DEFUN MEMBER (A X)
(COND
((NULL X) Nil)
((EQ A (CAR X)) T)
(T (MEMBER A (CDR X)) )
) )
(DEFUN UNION (X Y)
(COND
((NULL X) Y)
((MEMBER (CAR X) Y) (UNION (CDR X) Y) )
(T (CONS (CAR X) (UNION (CDR X) Y))) )) )
) )
(DEFUN INTERSECTION (X Y)
(COND
((NULL X) NIL)
((MEMBER (CAR X) Y)
(CONS (CAR X) (INTERSECTION (CDR X) Y)) )
(T (INTERSECTION (CDR X) Y))
) )
(INTERSECTION '(A1 A2 A3) '(A1 A3 A5))
(UNION '(X Y Z) '(U V W X))
Эта
Первые три формы сводятся к применению
Значение четвертой формы — (A1 A3). Значение пятой формы — (Y Z C B D X). Анализ пути, по которому выполняется рекурсия, показывает, почему элементы множества появляются именно в таком порядке.
В этом примере продемонстрировано несколько элементарных правил написания функциональных
((A . B) X (C . (E F D)))— допустимое S-выражение. Оно может быть записано как
((A . B) . ( X . ((C . (E . ( F . (D . Nil)))) . Nil)))или
((A . B) X (C E F D))
(A B C . D) есть сокращение для (A . ( B . ( C . D) )). Любая другая
расстановка точек на одном уровне есть ошибка, например (A. B C).Вывод S-выражений на печать и в файлы выполняет
(LOAD 'TEST.LST) (PRINT (INTERSECTION '(A1 A2 A3) '(Al A3 A5)) ) (PRINT (UNION '(X Y Z) '(U V W X)) ) (PRINT (UNION (READ) '(1 2 3 4)) ) ; объединение вводимого списка со списком ; '(1 2 3 4)
Таким образом, возвращаясь к Дж.Маккарти [1]:
"Можно написать "a + b, где a = 341 и b = 216". В такой ситуации не может быть недоразумений, и все согласятся, что ответ есть 557. Чтобы получить этот результат, необходимо заменить
переменные фактическими значениями, и затем сложить два числа (на арифмометре, например).
Одна из причин, по которой в этом случае не возникает недоразумений, состоит в том, что "a" и "b" не есть приемлемые входы для арифмометра, и следовательно, очевидно, что они только представляют
фактические аргументы . Присимвольных вычислениях ситуация может быть более сложной. Атом может быть какпеременной , так ифактическим аргументом . К дальнейшему усложнению приводит то обстоятельство, что некоторые из аргументов могут бытьпеременными , вычисляемыми внутри вызова другой функции. В таких случаях интуитивный подход может подвести. Для эффективного программирования в функциональном стиле необходимо более точное пониманиеформализмов .
Чтобы не обескураживать читателей, следует заметить, что здесь ничего принципиально нового нет. Все, что сейчас рассматривается, может быть логически выведено из правил представления
программ в виде S-выражений или из определенияуниверсальных функций eval/apply, и является их непосредственным следствием, возможно, не вполне очевидным.
Любой формализм для
переменных сводится к лямбда-обозначению. Часть интерпретатора, которая при вычислении функций связываетпеременные , называется APPLY. Когда APPLY встречает функцию, начинающуюся с LAMBDA, списокпеременных попарно связывается со списком аргументов и добавляется к началу а-списка. При вычислении функции могут быть обнаруженыпеременные . Они вычисляются поиском в а-списке. Еслипеременная встречается несколько раз, то используется последнее или самое новое значение. Часть интерпретатора, которая делает это, называется EVAL. Проиллюстрируем данное рассуждение на примере. Предположим, что интерпретатор получает следующее S-выражение:
((LAMBDA (X Y) (CONS X Y)) 'A 'B)
Функция:
((LAMBDA (X Y) (CONS X Y)) 'A 'B)
Аргументы:
(A B)
EVAL через EVAL-A передает эти аргументы функции APPLY. (См. лек. 3).
(APPLY #'(LAMBDA (X Y) (CONS X Y)) '(A B) Nil )
APPLY свяжет
переменные и передаст функцию и удлинившийся а-список EVAL для вычисления.
(EVAL '(CONS X Y) ' ((X . A) (Y . B) . Nil))
EVAL вычисляет
переменные и сразу передает ихконсолидации - функцииCONS , строящей из них бинарный узел.
(Cons 'A 'B) = (A . B)
EVAL вычисляет
переменные и сразу передает ихконсолидации , строящей из них бинарный узел.
Реальный интерпретатор пропускает один шаг, требуемый формальным определением
универсальных функций ".
На практике сложилась традиция включать в
(DEFUN UNION (x y)
(LET ( (a-x (CAR x))
(d-x (CDR x))
) ; конец списка локальных именованных значений
(COND ((NULL x) y)
((MEMBER a-x y) (UNION d-x y) ) ; использование локальных
(T (CONS a-x (UNION d-x y)) ) ; значений из контекста
) ) ; завершение контекста LET
)
(DEFUN MEMBER (a x)
(LET* ( (N-X (NULL x))
(a-x (CAR x))
(d-x (CDR x))
(e-car (EQ a a-x))
) ; список локально именованных выражений
(COND (N-X Nil) ; использование
(E-CAR T) ; именованных
(T (MEMBER A D-X)) ; выражений
) ) ; выход из контекста именованных выражений
)
(Эквивалентность с точностью до побочного эффекта.)
Глобальные
(DEFPARAMETER GLOB '(a b c))
Значение такой
(LET ((GLOB 12))(PRINT GLOB)) (PRINT GLOB)
напечатано будет:
12 (A B C)
Дж.Маккарти обращает внимание [1]:"Иногда говорят, что
Обычно (a . v) в конец a-списка. Но в реальных
(DefConstant X '(A B C D))
Особый интерес представляет тип
Ситуация, когда атом обозначает функцию, реализационно подобна той, в которой
атом обозначает аргумент. Если функция рекурсивна, то ей надо дать имя. Теоретически это
делается с помощью формы LABEL, которая связывает название с определением функции
в ассоциативном списке (а-списке). Название связано с определением функции точно
так же, как
Тот факт, что большинство функций —
(LABELS ( (INTERSECTION (x y)
(LET* ( (N-X (NULL x))
(MEM-CAR (MEMBER (CAR x) y))
(INT #'INTERSECTION)
) ; конец списка локальных выражений let*
(FLET ((F-TAIL (FN sx sy)
(APPLY FN (LIST (CDR sx) sy)) )
(CONS-F-TAIL (FN sx sy)
(CONS (CAR sx)
(APPLY FN (LIST (CDR sx) sy))
)) ) ; конец списка нерекурсивных функций FLET
(COND (N-X NIL) ; выражение, использующее
(MEM-CAR (cons-f-tail INT x y) ) ; локальные определения функций
(T (f-tail INT x y)) ) ; из контекстов FLET и
; LABELS
) ; выход из контекста FLET
) ; выход из контекста LET*
) ; завершено определение INTERSECTION
) ; конец списка локальных рекурсивных функций
(DEFUN UNION (x y)
(LET ( (a-x (CAR x))
(d-x (CDR x))
)
(COND ((NULL x) y)
((MEMBER a-x y) (UNION d-x y) )
(T (CONS a-x (UNION d-x y)) )
) ) ) ; завершено определение на текущем уровне
(INTERSECTION '(A1 A2 A3) '(A1 A3 A5))
(UNION '(X Y Z) '(U V W X))
) ; выход из контекста LABELS
Некоторые функции вместо определений с помощью S-выражений закодированы
как замкнутые машинные
Обычно EVAL вычисляет аргументы функций до применения к ним функций с помощью
APPLY. Таким образом, если EVAL задано (, то сначала вычисляются X и Y,
а потом над полученными значениями работает (QUOTE X),
то X не будет вычисляться. QUOTE —
Возможность использования безымянных определений функций не вполне очевидна
для вспомогательных рекурсивных функций, так как их имена нужны в их собственных
определениях. Но при необходимости и определение рекурсивной функции можно привести
к форме, не зависящей от ее имени. Такие имена формально работают как связанные
Пример (предложен В.А. Потапенко).
Преобразуем определение факториала в самоприменимую безымянную форму.
Для этого нужно:
Традиционное определение факториала:
(DEFUN N! (n)
(COND
((EQ n 0) 1)
(T (* n (N! (- n 1)))) ; Факториал
; Выход из рекурсии
; Рекурсивный виток с редукцией аргумента
) ) ; и умножением на результат предыдущего витка
Строим самоприменимое определение факториала:
(DEFUN N!_self (f n) ; Обобщенная функция,
; равносильная факториалу при f = N!_self
(COND ((EQ n 0)1)
(T (* n (APPLY f (list f (- n 1)))))
; применение функции f
; к списку из уменьшенного аргумента
) )
Использовать это определение можно следующим образом:
(N!_self #'N!_self 3) ; = 6 =
или
(APPLY 'N!_self '(N!_self 4)) ; = 24 =
При таких аргументах оно эквивалентно исходному определению факториала. Теперь избавимся от названия функции:
((LAMBDA (f n )
; безымянная функция, равносильная факториалу
; при f = N!_self
(COND ((EQ n 0)1)
(T (* n ( f (list f (- n 1)))))
))
Использовать это определение можно в следующей форме:
((LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) )) ; функция
(LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) )) ;первый аргумент — f
5 ; второй аргумент — n
) ; = 120 - результат самоприменения факториала
или
(APPLY
#'(LAMBDA (f n )
(COND ((EQ n 0)1)(T (* n (apply f (list f
(- n 1))))) ) ) ; функция
'((LAMBDA (f n )
(COND ((EQ n 0)1) (T (* n (apply f (list f
(- n 1))))) ))
6 ) ; список аргументов
)) ; = 720
Можно записать этот текст программы (код) без дублирования определения функции:
(LAMBDA (n)
( (LAMBDA (f) (APPLY f (list f n))))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(APPLY f (list f (- n 1)))) ) )
; внутренняя функция f
) ))
И использовать полученное определение следующим образом:
((LAMBDA (n)
((LAMBDA (f) (APPLY f (list f n)))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(APPLY f (list f (- n 1)))))
) ) )) 5 ) ; = 120 )
Сокращаем совпадающие подфункции и получаем форму, в которой основные содержательные компоненты локализованы:
((LAMBDA (n) (flet ((afl (f n)
(apply f (list f n)) ))
((LAMBDA (f) (afl f n))
#'(LAMBDA (f n ) (COND ((EQ n 0)1) (T (* n
(afl f (- n 1))))
)))))
6 ) ; = 720
)
Таким образом, определение рекурсивной функции можно преобразовать к
безымянной форме. Техника
Такое, пусть не самое понятное, определение позволяет получить больше, чем просто скорость исполнения кода и его переносимость. Техника функциональных определений и их преобразований позволяет рассматривать решение задачи с той точки зрения, с какой это удобно при постановке задачи, с естественной степенью подробности, гибкости и мобильности.
(FUNCALL F a1 a2 ... ) = (APPLY F (list a1 a2 ...))
Разрастание числа функций, манипулирующих применением функций в языке
Цель этой части — помочь избежать некоторых общих ошибок при отладке программ.
(CAR '(A B)) = (CAR (QUOTE(A B))
Функция: CAR
Аргументы: ((A B))
Значение есть A. Заметим, что интерпретатор ожидает (A B). Добавочная пара скобок возникает,
т.к. APPLY подается
Можно написать (LAMBDA(X)(CAR X)) вместо просто CAR. Это корректно, но
не является необходимым.
(CONS 'A '(B . C))
Функция:
Аргументы: (A (B . C))
Результат (A . (B . C)) (A B . C)
((CAR (QUOTE (A . B))) CDR (QUOTE (C . D)))
Функция:
Аргументы: ((CAR (QUOTE (A . B))) (
Значением такого вычисления будет
((CAR (QUOTE (A . B))) . (CDR (QUOTE (C . D))))
Скорее всего, это совсем не то, чего ожидал новичок. Он рассчитывал вместо (CAR (QUOTE (A . B)) получить A и увидеть (A . D) в качестве итогового значения
(CONS (CAR (QUOTE (A . B))) (CDR (QUOTE (C . D))) )
ниже приведены еще три правильных способа записи нужной формы.
Первый состоит в том, что CAR и
((LAMBDA (X Y) (CONS (CAR X) (CDR Y))) '(A . B) '(C . D))
Функция: (LAMBDA (X Y) (
Аргументы: ((A . B)(C . D))
(EVAL '(CONS (CAR (QUOTE (A . B)))
(CDR (QUOTE (C . D)))) Nil)
Функция: EVAL
Аргументы: ((
Значением того и другого является (A . D)
((LAMBDA (X Y) (CONS (EVAL X) (EVAL Y))) '(CAR (QUOTE (A . B)))' (CDR (QUOTE (C . D))) )
Функция: (LAMBDA (X Y) (
Аргументы: ((CAR (QUOTE (A . B))) (
Решения этого примера показывают, что грань между функциями и данными достаточно условна — одни и те же вычисления можно осуществить при разном распределении промежуточных вычислений внутри выражения, передвигая эту грань.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.