Противопоставление функционального и императивного (операторно-процедурного) стилей программирования порой напоминает свифтовские бои остроконечников с тупоконечниками. Впрочем, переписать функциональную программу в императивную проще, чем наоборот.
С практической точки зрения любые конструкции стандартных языков программирования могут быть введены как функции. Это делает их вполне легальными средствами в рамках функционального подхода. Надо лишь четко уяснить цену такого дополнения и его преимущества, обычно связанные с наследованием решений или с привлечением пользователей. В первых реализациях Лиспа были сразу предложены специальные формы и структуры данных, служащие мостом между разными стилями программирования. Они заодно смягчали на практике недостатки упрощенной схемы интерпретации S-выражений, выстроенной для учебных и исследовательских целей. Важнейшие средства такого рода, выдержавшие испытание временем, -
Рассмотрим предложенный Length. Эта функция сканирует список и вычисляет число элементов на верхнем уровне списка. Значение функции Length - целое число. Алгоритм можно описать следующими словами:
"Это функция одного аргумента L.
Она реализуется программой с двумя рабочими переменными z и v.
Записать число 0 в v.
Записать аргумент L в z.
A: Если z содержит NIL, то программа выполнена
и значением является то,
что сейчас записано в v.
Записать в z cdr от того, что сейчас в z.
Записать в v на единицу больше того, что сейчас записано в v.
Перейти к A"
Эту программу можно записать в виде Паскаль-программы с несколькими подходящими типами данных и функциями. Строкам вышеописанной программы соответствуют строки определения функции LENGTH, в предположении, что существует библиотека Лисп-функций на Паскале:
function LENGTH (L: list) : integer;
var Z: list;
V: integer;
begin
V := 0;
Z := l;
A: if null (Z) then LENGTH := V;
Z := cdr (Z);
V := V+1;
goto A;
end;
Переписывая в виде S -выражения, получаем программу:
(defun
LENGTH (lambda (L)
(prog (Z V)
(setq V 0)
(setq Z L)
A (cond ((null Z)(return V)))
(setq Z (cdr Z))
(setq V (+ 1 V))
(go A) ))) ))
;;=======================ТЕСТЫ=============
(LENGTH '(A B C D))
(LENGTH '((X . Y) A CAR (N B) (X Y Z)))
Последние две строки содержат тесты. Их значения 4 и 5 соответственно.
Форма Prog имеет структуру, подобную определениям функций и процедур в Паскале: ( PROG, список COND ".
Первый список после символа PROG называется списком NIL или (). С lambda. Значение каждой рабочей переменной есть NIL, до тех пор, пока ей не будет присвоено что-нибудь другое.
Для присваивания переменной применяется форма SET. Чтобы присвоить переменной pi значение 3.14 пишется:
(SET (QUOTE PI)3.14)
SETQ подобна SET, но она еще и блокирует вычисление первого аргумента. Поэтому
(SETQ PI 3.14)
запись того же присваивания. SETQ обычно удобнее. SET и SETQ могут изменять значения любых переменных из ассоциативного списка более внешних функций. Значением SET и SETQ является значение их второго аргумента.
GO-форма, используемая для указания перехода (GO A) указывает, что программа продолжается оператором, помеченным атомом A, причем это A может быть и из более внешнего prog.
Условные выражения в качестве операторов программы обладают полезными особенностями. Если ни один из предикатов не истинен, то программа продолжается оператором, следующим за условным выражением.
RETURN - нормальное завершение программы. Аргумент return вычисляется, что и является значением программы. Никакие последующие операторы не вычисляются.
Если программа прошла все свои операторы, не встретив Return, она завершается со значением NIL.
Prog-форма может быть рекурсивной.
| Лисп | Паскаль |
|---|---|
(DEFUN rev (x)
(prog (y z)
A (COND ((null x)(return y)))
(setq z (CDR x))
(COND ((ATOM z)(goto B)))
(setq z (rev z))
B (setq y (CONS z y))
(setq x (CDR x))
(goto A)
))
|
function rev (x: list) :List
var y, z: list;
begin
A: if null (x) Then rev := y;
z := cdr (x);
if atom (z) then goto B;
z := rev (z);
B: y := cons (z, y);
x := cdr (x);
goto A
end;
|
Функция rev обращает все уровни списка, так что rev от (A ((B C) D)) даст ((D (C B))A).
Для того, чтобы форма prog была полностью законна, необходима возможность дополнять go, set и return, не известные вне prog. (Формы Go, Set, Return работают как операторы лишь на верхнем уровне PROG или внутри COND, находящегося на верхнем уровне PROG. Но в современных версиях Лиспа их можно встретить и в других позициях.)
Атомы, выполняющие роль меток, работают как указатели помеченного блока.
Кроме того произошло уточнение механизма условных выражений, - отсутствие истинного предиката не препятствует формированию значения cond-оператора, т.к. все операторы игнорируют выработанное значение. Это позволяет считать, что при отсутствии истинного предиката значением условного выражения является Nil. Такое доопределение условного выражения давно перекочевало и в области обычных функций, где часто дает компактные формулы для рекурсии по списку. Исчезает необходимость в ветви вида " (T NIL) " .
В принципе SET и SETQ могут быть реализованы с помощью a-списка примерно также как и доступ к значению аргумента, только с копированием связей, расположенных ранее изменяемой переменной (см. функцию assign из параграфа 4). Более эффективная реализация, на основе списков свойств, будет описана ниже.
(DEFUN set (x y) (assign x y Alist))
Обратите внимание, что введенное таким образом присваивание работает разнообразнее, чем традиционное присваивание: допущена вычисляемость левой части присваивания, т.е. можно в программе вычислять имена переменных, значение которых предстоит поменять.
(setq x 'y) (set x 'NEW) (print x) (print y)
Напечатается Y и NEW.
Работа с циклами обеспечена в Лиспе достаточно традиционно.
(loop <форма>...)
Базовая форма цикла, представляющая собой встроенную функцию, многократно вычисляющую свои аргументы – тело цикла – до тех пор, пока на будет выполнен какой-либо явный выход из цикла, такой как RETURN.
(do(<параметры>...)(<предикат > < результат >...)
< форма >...)
(do*(<параметры >...)(<предикат > < результат >...)
< форма >...)
Обобщенные формы цикла, отличающиеся правилом связывания параметров цикла – независимо и последовательно.
(dolist (<переменная > < список > [<результат >] )
< форма >...)
Цикл, перебирающий список выражений, поочередно присваиваемых переменной цикла.
(dotimes (<переменная > < число > [<результат >] )
< форма >...)
Цикл, работающий заданное число шагов от 0 до N-1
< параметры > задаются как списки вида
(<переменная> <начальное_значение> [<шаг>] )
в котором:
< переменная > - символ с исходным значением Nil.
< начальное_значение > - начальное значение параметра.
< шаг > - выражение для вычисления параметра на каждом шаге цикла
<предикат> - ограничитель цикла
<результат> - результирующее выражение (при отсутствии - NIL)
<форма> - тело цикла, работает как неявная форма prog.
Значение дает последнее результирующее выражение.
(defun first-a (la)
;; самый левый атом
(setq x la)
(loop
(setq x (car x)) ; левый элемент структуры
(cond ((atom x)(return x)) )
; явный выход из цикла при обнаружении атома
) )
(print (first-a '(((123) 46) 5) ))
(defun len-do (ld)
;; длина списка
(do
((x ld (cdr x)); на каждом шаге переход к хвосту списка
(N 0 (1+ N))) ; подсчет числа шагов
((null x) N)) ; выход из цикла при пустом списке
)
(print (len-do '(1 2 3 4 5)))
(defun list-pa (lp)
(setq rl nil)
(dolist
(el lp rl) ; параметры перебора и результат
(setq rl (cons (cons el el) rl))
))
(print (list-pa '(a b c d))) ; = ((A . A)(B . B)(C . C)(D . D))
(defun ind-n (ln n)
(setq bl ln)
(setq ind nil)
(dotimes (i n ind)
(setq ind (cons (- n i) (cons (car bl ) ind )))
(setq bl (cdr bl))
))
(print (ind-n '(a b c d e f g) 4)) ; = D
| (Go Атом ) | Безусловный переход на оператор, помеченный Атомом |
| (Prog Атомы-или-Формы …) | Вычисляет последовательность форм в императивном стиле |
| (Prog1 Форма …) | Вычисляет формы, формальный результат – значение первой из них. |
| (Prog2 Форма …) | Вычисляет формы, формальный результат – значение второй из них. |
| (Progn Форма …) | Вычисляет формы, формальный результат – значение последней из них. |
| (Return Форма ) | Результат и завершение формы Prog |
| (Do (var ...) ( expr rez ...) expr ...) | Цикл с последовательным заданием параметров и выходом по заданному условию |
| (Do* (var ...) ( expr rez ...) expr ...) | Цикл с параллельным заданием параметров и выходом по заданному условию |
| (Dolist (var list [rez] ) expr ...) | Цикл перебора значений параметра из списка |
| (Dotimes (var number [rez] ) expr ...) | Цикл, работающий заданное число раз |
| (Loop expr ...) | Цикл работающий до внутреннего Return |
Противопоставление функционального и императивного (операторно-процедурного) стилей программирования порой напоминает свифтовские бои остроконечников с тупоконечниками. Впрочем, переписать функциональную программу в императивную проще, чем наоборот.
С практической точки зрения любые конструкции стандартных языков программирования могут быть введены как функции. Это делает их вполне легальными средствами в рамках функционального подхода. Надо лишь четко уяснить цену такого дополнения и его преимущества, обычно связанные с наследованием решений или с привлечением пользователей. В первых реализациях Лиспа были сразу предложены специальные формы и структуры данных, служащие мостом между разными стилями программирования. Они заодно смягчали на практике недостатки упрощенной схемы интерпретации S-выражений, выстроенной для учебных и исследовательских целей. Важнейшие средства такого рода, выдержавшие испытание временем, -
Рассмотрим предложенный Length. Эта функция сканирует список и вычисляет число элементов на верхнем уровне списка. Значение функции Length - целое число. Алгоритм можно описать следующими словами:
"Это функция одного аргумента L.
Она реализуется программой с двумя рабочими переменными z и v.
Записать число 0 в v.
Записать аргумент L в z.
A: Если z содержит NIL, то программа выполнена
и значением является то,
что сейчас записано в v.
Записать в z cdr от того, что сейчас в z.
Записать в v на единицу больше того, что сейчас записано в v.
Перейти к A"
Эту программу можно записать в виде Паскаль-программы с несколькими подходящими типами данных и функциями. Строкам вышеописанной программы соответствуют строки определения функции LENGTH, в предположении, что существует библиотека Лисп-функций на Паскале:
function LENGTH (L: list) : integer;
var Z: list;
V: integer;
begin
V := 0;
Z := l;
A: if null (Z) then LENGTH := V;
Z := cdr (Z);
V := V+1;
goto A;
end;
Переписывая в виде S -выражения, получаем программу:
(defun
LENGTH (lambda (L)
(prog (Z V)
(setq V 0)
(setq Z L)
A (cond ((null Z)(return V)))
(setq Z (cdr Z))
(setq V (+ 1 V))
(go A) ))) ))
;;=======================ТЕСТЫ=============
(LENGTH '(A B C D))
(LENGTH '((X . Y) A CAR (N B) (X Y Z)))
Последние две строки содержат тесты. Их значения 4 и 5 соответственно.
Форма Prog имеет структуру, подобную определениям функций и процедур в Паскале: ( PROG, список COND ".
Первый список после символа PROG называется списком NIL или (). С lambda. Значение каждой рабочей переменной есть NIL, до тех пор, пока ей не будет присвоено что-нибудь другое.
Для присваивания переменной применяется форма SET. Чтобы присвоить переменной pi значение 3.14 пишется:
(SET (QUOTE PI)3.14)
SETQ подобна SET, но она еще и блокирует вычисление первого аргумента. Поэтому
(SETQ PI 3.14)
запись того же присваивания. SETQ обычно удобнее. SET и SETQ могут изменять значения любых переменных из ассоциативного списка более внешних функций. Значением SET и SETQ является значение их второго аргумента.
GO-форма, используемая для указания перехода (GO A) указывает, что программа продолжается оператором, помеченным атомом A, причем это A может быть и из более внешнего prog.
Условные выражения в качестве операторов программы обладают полезными особенностями. Если ни один из предикатов не истинен, то программа продолжается оператором, следующим за условным выражением.
RETURN - нормальное завершение программы. Аргумент return вычисляется, что и является значением программы. Никакие последующие операторы не вычисляются.
Если программа прошла все свои операторы, не встретив Return, она завершается со значением NIL.
Prog-форма может быть рекурсивной.
| Лисп | Паскаль |
|---|---|
(DEFUN rev (x)
(prog (y z)
A (COND ((null x)(return y)))
(setq z (CDR x))
(COND ((ATOM z)(goto B)))
(setq z (rev z))
B (setq y (CONS z y))
(setq x (CDR x))
(goto A)
))
|
function rev (x: list) :List
var y, z: list;
begin
A: if null (x) Then rev := y;
z := cdr (x);
if atom (z) then goto B;
z := rev (z);
B: y := cons (z, y);
x := cdr (x);
goto A
end;
|
Функция rev обращает все уровни списка, так что rev от (A ((B C) D)) даст ((D (C B))A).
Для того, чтобы форма prog была полностью законна, необходима возможность дополнять go, set и return, не известные вне prog. (Формы Go, Set, Return работают как операторы лишь на верхнем уровне PROG или внутри COND, находящегося на верхнем уровне PROG. Но в современных версиях Лиспа их можно встретить и в других позициях.)
Атомы, выполняющие роль меток, работают как указатели помеченного блока.
Кроме того произошло уточнение механизма условных выражений, - отсутствие истинного предиката не препятствует формированию значения cond-оператора, т.к. все операторы игнорируют выработанное значение. Это позволяет считать, что при отсутствии истинного предиката значением условного выражения является Nil. Такое доопределение условного выражения давно перекочевало и в области обычных функций, где часто дает компактные формулы для рекурсии по списку. Исчезает необходимость в ветви вида " (T NIL) " .
В принципе SET и SETQ могут быть реализованы с помощью a-списка примерно также как и доступ к значению аргумента, только с копированием связей, расположенных ранее изменяемой переменной (см. функцию assign из параграфа 4). Более эффективная реализация, на основе списков свойств, будет описана ниже.
(DEFUN set (x y) (assign x y Alist))
Обратите внимание, что введенное таким образом присваивание работает разнообразнее, чем традиционное присваивание: допущена вычисляемость левой части присваивания, т.е. можно в программе вычислять имена переменных, значение которых предстоит поменять.
(setq x 'y) (set x 'NEW) (print x) (print y)
Напечатается Y и NEW.
Работа с циклами обеспечена в Лиспе достаточно традиционно.
(loop <форма>...)
Базовая форма цикла, представляющая собой встроенную функцию, многократно вычисляющую свои аргументы – тело цикла – до тех пор, пока на будет выполнен какой-либо явный выход из цикла, такой как RETURN.
(do(<параметры>...)(<предикат > < результат >...)
< форма >...)
(do*(<параметры >...)(<предикат > < результат >...)
< форма >...)
Обобщенные формы цикла, отличающиеся правилом связывания параметров цикла – независимо и последовательно.
(dolist (<переменная > < список > [<результат >] )
< форма >...)
Цикл, перебирающий список выражений, поочередно присваиваемых переменной цикла.
(dotimes (<переменная > < число > [<результат >] )
< форма >...)
Цикл, работающий заданное число шагов от 0 до N-1
< параметры > задаются как списки вида
(<переменная> <начальное_значение> [<шаг>] )
в котором:
< переменная > - символ с исходным значением Nil.
< начальное_значение > - начальное значение параметра.
< шаг > - выражение для вычисления параметра на каждом шаге цикла
<предикат> - ограничитель цикла
<результат> - результирующее выражение (при отсутствии - NIL)
<форма> - тело цикла, работает как неявная форма prog.
Значение дает последнее результирующее выражение.
(defun first-a (la)
;; самый левый атом
(setq x la)
(loop
(setq x (car x)) ; левый элемент структуры
(cond ((atom x)(return x)) )
; явный выход из цикла при обнаружении атома
) )
(print (first-a '(((123) 46) 5) ))
(defun len-do (ld)
;; длина списка
(do
((x ld (cdr x)); на каждом шаге переход к хвосту списка
(N 0 (1+ N))) ; подсчет числа шагов
((null x) N)) ; выход из цикла при пустом списке
)
(print (len-do '(1 2 3 4 5)))
(defun list-pa (lp)
(setq rl nil)
(dolist
(el lp rl) ; параметры перебора и результат
(setq rl (cons (cons el el) rl))
))
(print (list-pa '(a b c d))) ; = ((A . A)(B . B)(C . C)(D . D))
(defun ind-n (ln n)
(setq bl ln)
(setq ind nil)
(dotimes (i n ind)
(setq ind (cons (- n i) (cons (car bl ) ind )))
(setq bl (cdr bl))
))
(print (ind-n '(a b c d e f g) 4)) ; = D
| (Go Атом ) | Безусловный переход на оператор, помеченный Атомом |
| (Prog Атомы-или-Формы …) | Вычисляет последовательность форм в императивном стиле |
| (Prog1 Форма …) | Вычисляет формы, формальный результат – значение первой из них. |
| (Prog2 Форма …) | Вычисляет формы, формальный результат – значение второй из них. |
| (Progn Форма …) | Вычисляет формы, формальный результат – значение последней из них. |
| (Return Форма ) | Результат и завершение формы Prog |
| (Do (var ...) ( expr rez ...) expr ...) | Цикл с последовательным заданием параметров и выходом по заданному условию |
| (Do* (var ...) ( expr rez ...) expr ...) | Цикл с параллельным заданием параметров и выходом по заданному условию |
| (Dolist (var list [rez] ) expr ...) | Цикл перебора значений параметра из списка |
| (Dotimes (var number [rez] ) expr ...) | Цикл, работающий заданное число раз |
| (Loop expr ...) | Цикл работающий до внутреннего Return |
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.