Скачать презентацию Мир Лиспа — 2 Другие функции ветвления Скачать презентацию Мир Лиспа — 2 Другие функции ветвления

Лисп_2.ppt

  • Количество слайдов: 83

Мир Лиспа - 2 Мир Лиспа - 2

Другие функции ветвления (if условие если_истина если_ложь) (when условие форма-1 форма-2. . . ) Другие функции ветвления (if условие если_истина если_ложь) (when условие форма-1 форма-2. . . ) (unless условие форма-1 форма-2. . . )

4. Числа Фибоначчи Последовательность Фибоначчи задается следующим образом: F 1=1 F 2=1 Fn=Fn-1+Fn-2 Попробуем 4. Числа Фибоначчи Последовательность Фибоначчи задается следующим образом: F 1=1 F 2=1 Fn=Fn-1+Fn-2 Попробуем реализовать ее на Лиспе

Первое решение (defun fib-s (n) (cond ((< n 2) 1) (t (+ (fib-s (- Первое решение (defun fib-s (n) (cond ((< n 2) 1) (t (+ (fib-s (- n 1)) (fib-s (- n 2))))))) fib-s (fib-s 8) 21 (fib-s 20) 6765 Хорошее ли это решение?

Запустим трассировку. . . (trace fib-s) fib-s (fib-s 5) Вход в функцию fib-s Аргументы: Запустим трассировку. . . (trace fib-s) fib-s (fib-s 5) Вход в функцию fib-s Аргументы: 5 Вход в функцию fib-s Аргументы: 4 Вход в функцию fib-s Аргументы: 3 Вход в функцию fib-s Аргументы: 2 Возврат из функции fib-s Результат: 1 Вход в функцию fib-s Аргументы: 1 Возврат из функции fib-s Результат: 2 Вход в функцию fib-s Аргументы: 2 Возврат из функции fib-s Результат: 1 Возврат из функции fib-s Результат: 3 Вход в функцию fib-s Аргументы: 2 Возврат из функции fib-s Результат: 1 Вход в функцию fib-s Аргументы: 1 Возврат из функции fib-s Результат: 2 Возврат из функции fib-s Результат: 5 5

Плохое решение! Видно, что алгоритм крайне неэффективен: происходит многократный пересчет функции при одних и Плохое решение! Видно, что алгоритм крайне неэффективен: происходит многократный пересчет функции при одних и тех же значениях аргумента. Кстати, а как сосчитать, сколько происходит рекурсивных вызовов в процессе вычисления?

Статистика выполнения В Home. Lisp есть функции stat и unstat – включение и выключение Статистика выполнения В Home. Lisp есть функции stat и unstat – включение и выключение режима статистики. Включим статистику и рассчитаем 20 -е число Фибоначчи. . .

(stat) T (fib-s 20) 6765 *** Статистика обращений *** +. . . 6764 -. (stat) T (fib-s 20) 6765 *** Статистика обращений *** +. . . 6764 -. . . 13528 <=. . . 13529 FIB-S. . . . . 13529 COND. . . . . 13529

А теперь – рациональное решение: (defun fib-n (n &optional (curr 1) (prev 1)) (cond А теперь – рациональное решение: (defun fib-n (n &optional (curr 1) (prev 1)) (cond ((= n 2) curr) (t (fib-n (- n 1) (+ curr prev) curr)))) В функции 2 накапливающих параметра. Функция остаётся рекурсивной; посмотрим, как обстоит дело с производительностью. . .

Пробуем. . . (fib-n 20) 6765 ; ; результат верный ! *** Статистика обращений Пробуем. . . (fib-n 20) 6765 ; ; результат верный ! *** Статистика обращений *** -. . . 18 +. . . 18 <=. . . 19 FIB-N. . . . . 19 COND. . . . . 19 Комментарии излишни. . .

Создание локальных переменных Функции LET/LET* Создание локальных переменных Функции LET/LET*

Общий вид Let: (let ((имя-1 значение-1) (имя-2 значение-2). . . (имя-n значение-n)) (форма-1) (форма-2). Общий вид Let: (let ((имя-1 значение-1) (имя-2 значение-2). . . (имя-n значение-n)) (форма-1) (форма-2). . . (форма-m)) Создаются переменные, которые “живут”в теле Let.

В процессе вычисления LET все локальные переменные вычисляются “одновременно”: (Let ((x 1) (y (* В процессе вычисления LET все локальные переменные вычисляются “одновременно”: (Let ((x 1) (y (* x x)) ; ; здесь будет ошибка ! …) У функции LET* локальные переменные вычисляются “последовательно”: (Let* ((x 1) (y (* x x)) ; ; все хорошо ! …)

5. Итерационные циклы Рассмотрим классическую задачу: Вычислить с заданной точностью eps при заданном x 5. Итерационные циклы Рассмотрим классическую задачу: Вычислить с заданной точностью eps при заданном x сумму ряда: Σxn/n! (кстати, чему она равна? ) правильно = ex

Как рационально решить задачу? Нерадивые студенты “кинутся” вычислять n! - и зря! Рассмотрим, чем Как рационально решить задачу? Нерадивые студенты “кинутся” вычислять n! - и зря! Рассмотрим, чем отличаются друг от друга n-й и (n+1)-й члены ряда: Qn=xn/n! а Qn+1=x(n+1)/(n+1)! Qn+1/Qn=x/(n+1) т. е. , чтобы вычислить следующий член ряда, нужно взять предыдущий, умножить на x и разделить на (n+1)

Вот функция: (defun aexp (x eps &optional (s 0) (n 0) (q 1)) (let Вот функция: (defun aexp (x eps &optional (s 0) (n 0) (q 1)) (let ((z (/ (* q x) (+ n 1))) (s (+ s z))) (cond ((< z eps) s) ; ; терм. ветвь (t (aexp x eps s (+ n 1) z))))) s, n, q – накопительные параметры. Функция рекурсивная; завершение рекурсии происходит при достижении заданной точности eps.

Пробуем функцию “в деле”: (aexp 1 1 E-14) 2. 71828459040 E+0 (exp 1) 2. Пробуем функцию “в деле”: (aexp 1 1 E-14) 2. 71828459040 E+0 (exp 1) 2. 71828459050 E+0

Небольшое дополнение Некоторые устаревшие реализации Лиспа не поддерживаю &optional – параметры. Как же в Небольшое дополнение Некоторые устаревшие реализации Лиспа не поддерживаю &optional – параметры. Как же в таких реализациях использовать технику накапливающих параметров?

В этом случае делаем накапливающие параметры обязательными, отлаживаем функцию, задавая начальные значения накапливающих параметров В этом случае делаем накапливающие параметры обязательными, отлаживаем функцию, задавая начальные значения накапливающих параметров явно, а затем “погружаем” нашу функцию в функцию-обертку (служащую только для запуска).

; ; Главная функция (defun exp_ (x eps s n q ) (let ((q ; ; Главная функция (defun exp_ (x eps s n q ) (let ((q (/ (* q x) (+ n 1))) (s (+ s q))) (cond ((< q eps) s) (t (exp_ x eps s (+ n 1) q))))) ; ; Функция - обертка (defun aexp (x eps) (exp_ x eps 0 0 1))

Безымянные функции Безымянные функции

Это конструкция вида: (lambda (список_параметров). . . тело_функции ; ; 1 или более S-выражений Это конструкция вида: (lambda (список_параметров). . . тело_функции ; ; 1 или более S-выражений ) Отличие от defun – в замене defun на lambda и отсутствии имени функции

Как этим пользоваться? Вызов обычной (именованной) функции: (Имя_функции Параметры) Вызов безымянной функции: (Лямбда-выражение Параметры) Как этим пользоваться? Вызов обычной (именованной) функции: (Имя_функции Параметры) Вызов безымянной функции: (Лямбда-выражение Параметры)

Примеры: ((lambda (x y) (* x y)) 7 8) ? 56 ((lambda (x y Примеры: ((lambda (x y) (* x y)) 7 8) ? 56 ((lambda (x y &optional r) (list x y r)) ‘a ‘b) ? (a b Nil) Лямбда-выражение может стоять в любом месте, где допустим вызов функции.

Для чего нужны безымянные функции? Для экономии функций именованных. Может ли безымянная функция быть Для чего нужны безымянные функции? Для экономии функций именованных. Может ли безымянная функция быть рекурсивной? Вопрос интересный…

А ответ, тем не менее, положителен! Рассмотрим сначала традиционную функцию вычисления факториала: (defun fact А ответ, тем не менее, положителен! Рассмотрим сначала традиционную функцию вычисления факториала: (defun fact (n) (cond ((= n 0) 1) (t (* n (fact (- n 1))))))

Проверим: (fact 100) 93326215443944152681699238856266700490 71596826438162146859296389521759999322 99156089414639761565182862536979208272 23758251185210916864000000000 Проверим: (fact 100) 93326215443944152681699238856266700490 71596826438162146859296389521759999322 99156089414639761565182862536979208272 23758251185210916864000000000

((lambda (n f) (cond ((= n 0) 1) (t (* n (funcall f (- ((lambda (n f) (cond ((= n 0) 1) (t (* n (funcall f (- n 1) f))))) 10 '(lambda (n f) (cond ((= n 0) 1) (t (* n (funcall f (- n 1) f)))))) 3628800

Функции высшего порядка (функционалы) Функции высшего порядка (функционалы)

Функциональные языки (ФЯ) отличаются от нефункциональных тем, что в ФЯ функции являются полноправными значениями, Функциональные языки (ФЯ) отличаются от нефункциональных тем, что в ФЯ функции являются полноправными значениями, т. е. могут передаваться в другие функции и возвращаться как результат вычисления. Функция, принимающая одну (или более) функций как аргумент (аргументы), называется функционалом.

Пример функционала: Есть числовой список. Нужно построить список, состоящий из квадратов исходного списка. (defun Пример функционала: Есть числовой список. Нужно построить список, состоящий из квадратов исходного списка. (defun q 2 (x) (cond ((null x) Nil) (t (cons (^ (car x) 2) (q 2 (cdr x)))))) q 2 (q 2 '(1 2 3 4 5 6)) (1 4 9 16 25 36) всё хорошо. . . А если теперь нужно возводить в куб?

Попробуем “приделать” нашей функции функциональный аргумент: (defun aq (f x) (cond ((null x) Nil) Попробуем “приделать” нашей функции функциональный аргумент: (defun aq (f x) (cond ((null x) Nil) (t (cons (f (car x)) (q 2 (cdr x)))))) Как вызвать такую функцию? (aq ‘(lambda (x) (* x x)) ‘(1 2 3 4 5 6)) ; ; в квадрат (aq ‘(lambda (x) (* x x x)) ‘(1 2 3 4 5 6)) ; ; в куб (aq ‘ff ‘(1 2 3 4 5 6)) ; ; ff произв. функция одного аргумента

К сожалению, есть проблема: В Лиспе нельзя применить функциональный аргумент так, как мы пытаемся: К сожалению, есть проблема: В Лиспе нельзя применить функциональный аргумент так, как мы пытаемся: (defun aq (f x) (cond ((null x) Nil) (t (cons (f (car x)) (aq f (cdr x)))))) ; ; ошибка!!! Для применения функционального аргумента служит специальная функция FUNCALL: (defun aq (f x) (cond ((null x) Nil) (t (cons (funcall f (car x)) (aq f (cdr x)))))) ; ; верно!

Funcall Функционал funcall принимает неопределенное количество аргументов. Он применяет свой первый (функциональный) аргумент к Funcall Функционал funcall принимает неопределенное количество аргументов. Он применяет свой первый (функциональный) аргумент к списку, составленному из всех остальных аргументов. Естественно, количество аргументов должно быть “правильным” (соответствовать списку параметров функционального аргумента).

Испытаем функцию AQ: (defun aq (f x) (cond ((null x) Nil) (t (cons (funcall Испытаем функцию AQ: (defun aq (f x) (cond ((null x) Nil) (t (cons (funcall f (car x)) (aq f (cdr x)))))) aq (aq '(lambda (x) (* x x)) '(1 2 3 4 5 6)) ; ; в квадрат (1 4 9 16 25 36) (aq '(lambda (x) (* x x x)) '(1 2 3 4 5 6)) ; ; в куб (1 8 27 64 125 216) (aq 'sin '(1 2 3 4 5 6)) ; ; синусы (8. 414709848078970 E-1 9. 092974268256820 E-1 1. 411200080598670 E-1 -7. 568024953079280 E-1 9. 589242746631380 E-1 -2. 794154981989260 E-1)

Функционал, который применяет функциональный аргумент к задаваемым параметрам, называется применяющим. Funcall – применяющий функционал. Функционал, который применяет функциональный аргумент к задаваемым параметрам, называется применяющим. Funcall – применяющий функционал. Другой стандартный применяющий функционал Лиспа – это Apply

Функционал Apply: Принимает два аргумента: первый функциональный, а второй – список произвольной длины. Apply Функционал Apply: Принимает два аргумента: первый функциональный, а второй – список произвольной длины. Apply применяет свой функциональный аргумент к этому списку. Разница между Funcall и Apply состоит только в одном: Funcall требует аргументы “врассыпную”, а Apply – собранными в список.

Поскольку стандартная функции * (times) и + (plus) в Лиспе принимают неопределенное число аргументов, Поскольку стандартная функции * (times) и + (plus) в Лиспе принимают неопределенное число аргументов, то самое простое решение задачи о сумме (произведении) элементов произвольного числового списка это применение Apply (вообще без рекурсии!)

(defun multlist (x) (apply '* x)) multlist (multlist '(1 2 3 4 5 6 (defun multlist (x) (apply '* x)) multlist (multlist '(1 2 3 4 5 6 7 8 9 10 11 12 13)) 6227020800 (defun sumlist (x) (apply '+ x)) sumlist (sumlist '(1 2 3 4 5 6 7 8 9 10 11 12 13)) 91

Сортировка выбором (defun vsort (lst) (cond ((null lst) Nil) (t (let ((min (apply ‘min Сортировка выбором (defun vsort (lst) (cond ((null lst) Nil) (t (let ((min (apply ‘min lst))) (cons min (vsort (removef min lst)))))) (vsort '(1 2 3 4 1 2 3 -7)) (-7 1 1 2 2 3 3 4)

Отображающие функционалы: Отображающие функционалы применяют (определенным образом) свой функциональный аргумент к списку или его Отображающие функционалы: Отображающие функционалы применяют (определенным образом) свой функциональный аргумент к списку или его части и возвращают новый список. Этот вид функционалов “отображает” исходный список на результирующий. Отсюда и название.

Простейший отображающий функционал - mapcar Фунционал mapcar принимает два аргумента: первый функциональный, второй – Простейший отображающий функционал - mapcar Фунционал mapcar принимает два аргумента: первый функциональный, второй – список произвольной длины. Работа функционала состоит в последовательном применении функционального аргумента к первому элементу списка, второму и т. д. Полученные результаты вновь объединяются в список.

Применение mapcar значительно упрощает решение уже рассмотренной ранее задачи о списке квадратов, кубов и Применение mapcar значительно упрощает решение уже рассмотренной ранее задачи о списке квадратов, кубов и т. д. При этом даже не требуется явная рекурсия: (mapcar '(lambda (x) (* x x)) '(1 2 3 4 5 6)) (1 4 9 16 25 36) (mapcar '(lambda (x) (* x x x)) '(1 2 3 4 5 6)) (1 8 27 64 125 216)

Еще один отображающий функционал maplist Функционал maplist последовательно применяет свой первый функциональный аргумент ко Еще один отображающий функционал maplist Функционал maplist последовательно применяет свой первый функциональный аргумент ко всему списку (заданному вторым аргументом), к хвосту списка и т. д. Результаты объединяются в список. Функция, задаваемая функциональным аргументом, должна принимать список.

Пример вызова maplist: (maplist '(lambda (x) (apply '* x)) '(1 2 3 4 5 Пример вызова maplist: (maplist '(lambda (x) (apply '* x)) '(1 2 3 4 5 6)) (720 360 120 30 6) Здесь лямбда-выражение сначала применяется к списку (1 2 3 4 5 6) и возвращает 6!=720; далее оно применяется к списку (2 3 4 5 6) и снова возвращает 720; затем применяется к списку (3 4 5 6) и дает 360 и т. д.

Поскольку maplist написана на Лиспе, ее можно протрассировать: (trace maplist) maplist (maplist '(lambda (x) Поскольку maplist написана на Лиспе, ее можно протрассировать: (trace maplist) maplist (maplist '(lambda (x) (apply '* x)) '(1 2 3 4 5 6)) Вход в функцию maplist Аргументы: (LAMBDA (x) (APPLY (QUOTE *) x)) (1 2 3 4 5 6) Вход в функцию maplist Аргументы: (LAMBDA (x) (APPLY (QUOTE *) x)) (3 4 5 6) Вход в функцию maplist Аргументы: (LAMBDA (x) (APPLY (QUOTE *) x)) (5 6) Вход в функцию maplist Аргументы: (LAMBDA (x) (APPLY (QUOTE *) x)) (6) Вход в функцию maplist Аргументы: (LAMBDA (x) (APPLY (QUOTE *) x)) NIL Возврат из функции maplist Результат: (6) Возврат из функции maplist Результат: (30 6) Возврат из функции maplist Результат: (120 30 6) Возврат из функции maplist Результат: (360 120 30 6) Возврат из функции maplist Результат: (720 720 360 120 30 6) (720 360 120 30 6)

Поскольку maplist в процессе работы получает доступ ко всем элементам списка (или его остатков), Поскольку maplist в процессе работы получает доступ ко всем элементам списка (или его остатков), а не только к первым элементам (как mapcar), то maplist является и более универсальной функцией, чем mapcar. В частности, mapcar можно выразить через maplist.

Мощь mapcar Попробуем вычислить скалярное произведение двух векторов, заданных числовыми списками одинаковой длины: (v Мощь mapcar Попробуем вычислить скалярное произведение двух векторов, заданных числовыми списками одинаковой длины: (v 1 v 2 v 3. . . ) и (w 1 w 2 w 3. . . )

Mapcar на самом деле может принимать не один, а произвольное количество списков. В этом Mapcar на самом деле может принимать не один, а произвольное количество списков. В этом случае он (mapcar) работает так:

Если мы напишем вызов: (mapcar ‘* ‘(v 1 v 2 v 3) ‘(w 1 Если мы напишем вызов: (mapcar ‘* ‘(v 1 v 2 v 3) ‘(w 1 w 2 w 3)) то получим: (v 1*w 1 v 2*w 2 v 3*w 3)

Теперь нужно подсчитать сумму этого списка. Как это сделать? С помощью Apply! (apply ‘+ Теперь нужно подсчитать сумму этого списка. Как это сделать? С помощью Apply! (apply ‘+ (mapcar ‘* (v 1 v 2 v 3) (w 1 w 2 w 3)))

Окончательное решение: (defun scal-prod (v w) (apply ‘+ (mapcar ‘* v w))) Этот код Окончательное решение: (defun scal-prod (v w) (apply ‘+ (mapcar ‘* v w))) Этот код будет правильно считать скалярное произведение векторов любой размерности!

Другие стандартные функционалы: remove-if - удаление из списка по соблюдению условия; remove-if-not - удаление Другие стандартные функционалы: remove-if - удаление из списка по соблюдению условия; remove-if-not - удаление из списка по нарушению условия; every – проверка истинности предиката на всех элементах списка; reduce – свертка;

Функционал every Этот функционал не является отображающим. Первым аргументом every должен быть предикат (логическая Функционал every Этот функционал не является отображающим. Первым аргументом every должен быть предикат (логическая функция, вычисляющаяся до T/Nil). Вторым аргументом должен быть произвольный список. Если условие, задаваемое предикатом, истинно для каждого элемента списка, every вернет T, иначе – Nil.

Примеры: (oddp 1) T (oddp 2) NIL (every 'oddp '(1 2 3 4)) NIL Примеры: (oddp 1) T (oddp 2) NIL (every 'oddp '(1 2 3 4)) NIL (every 'oddp '(1 21 31 41)) T

Функционалы remove-if [-not] Этот функционал использует первый (функциональный) аргумент как предикат и применяет его Функционалы remove-if [-not] Этот функционал использует первый (функциональный) аргумент как предикат и применяет его последовательно к каждому элементу списка-второго аргумента. Если элемент удовлетворяет (не удовлетворяет) предикату – элемент попадает в выходной список. remove-if и remove-if-not – отображающие функционалы (аналогичные mapcar).

Элементарные примеры: (remove-if 'oddp '(1 2 3 4 5 6 7 8 9)) (2 Элементарные примеры: (remove-if 'oddp '(1 2 3 4 5 6 7 8 9)) (2 4 6 8) (remove-if-not 'oddp '(1 2 3 4 5 6 7 8 9)) (1 3 5 7 9) (remove-if '(lambda (x) (= 0 (% x 3))) '(1 2 3 4 5 6 7 8 9)) (1 2 4 5 7 8)

Дополнительные параметры remove-if-. . . У этих функционалов есть еще два дополнительных (необязательных) ключевых Дополнительные параметры remove-if-. . . У этих функционалов есть еще два дополнительных (необязательных) ключевых параметра: : count n - сколько элементов удалять; : from-end [t/nil] – удалять с конца или с начала списка.

Примеры: (remove-if '(lambda (x) (= 0 (% x 3))) '(1 2 3 4 5 Примеры: (remove-if '(lambda (x) (= 0 (% x 3))) '(1 2 3 4 5 6 7 8 9) : count 1) (1 2 4 5 6 7 8 9) (remove-if '(lambda (x) (= 0 (% x 3))) '(1 2 3 4 5 6 7 8 9) : count 1 : from-end t) (1 2 3 4 5 6 7 8)

Свертка - reduce Этот функционал применяет свой первый (функциональный) аргумент сначала к первым двум Свертка - reduce Этот функционал применяет свой первый (функциональный) аргумент сначала к первым двум элементам списка, затем к этому результату и третьему элементу и т. д. до исчерпания списка. Результат последнего вычисления возвращается, как результат свертки.

Примеры: (reduce '* '(1 2 3 4)) 24 (reduce 'max '(1 2 3 4 Примеры: (reduce '* '(1 2 3 4)) 24 (reduce 'max '(1 2 3 4 5 4 3 2 1)) 5 (reduce 'list '(1 2 3 4 5)) ((((1 2) 3) 4) 5) (reduce 'append '((1 2) (3 4) (5 6))) (1 2 3 4 5 6)

Необязательные параметры reduce : initial-value v - задает первое значение, c которого начинается свертка. Необязательные параметры reduce : initial-value v - задает первое значение, c которого начинается свертка. (reduce '* '(1 2 3) : initial-value 5) 30 (reduce 'max '(1 2 3 4 5 4 3 2 1) : initial-value 100) 100

Продолжение. . . (reduce 'append '((1) (2) (3) (4) ) : initial-value '(b e Продолжение. . . (reduce 'append '((1) (2) (3) (4) ) : initial-value '(b e g)) (b e g 1 2 3 4) Поддерживается также и параметр : from-end (reduce 'append '((1) (2) (3) (4) ) : initial-value '(b e g) : from-end t) (1 2 3 4 b e g)

Существует еще много других стандартных функционалов. . . Существует еще много других стандартных функционалов. . .

Замыкания Замыкания

“Плохая” функция setq Функция setq позволяет явно присваивать значения переменным: (setq a ‘(1 2 “Плохая” функция setq Функция setq позволяет явно присваивать значения переменным: (setq a ‘(1 2 3)) (1 2 3) a (1 2 3)

До сих пор нам вполне удавалось обходиться без явных присвоений. Там, где требовались рабочие До сих пор нам вполне удавалось обходиться без явных присвоений. Там, где требовались рабочие переменные, мы использовали функцию let.

Общий вид Let: (let ((имя-1 значение-1) (имя-2 значение-2). . . (имя-n значение-n)) (форма-1) (форма-2). Общий вид Let: (let ((имя-1 значение-1) (имя-2 значение-2). . . (имя-n значение-n)) (форма-1) (форма-2). . . (форма-m)) Создаются переменные, которые “живут”в теле Let.

Что будет, если в теле let определить одну или более функций? Что будет, если в теле let определить одну или более функций?

Попробуем: (let ((c 0)) (defun next-1 nil (setq c (+ 1 c))) ) Переменная Попробуем: (let ((c 0)) (defun next-1 nil (setq c (+ 1 c))) ) Переменная c свободна (относительно тела nехт)

(let ((c 0)) (defun next-1 nil (setq c (+ 1 c))) ) next-1 (next-1) (let ((c 0)) (defun next-1 nil (setq c (+ 1 c))) ) next-1 (next-1) 2 (next-1) 3 c Assoc: Символ c не имеет значения (не связан). ERRSTATE

Еще один эксперимент: (let ((c 0)) (defun next-2 nil (setq c (+ 1 c))) Еще один эксперимент: (let ((c 0)) (defun next-2 nil (setq c (+ 1 c))) ) next-2 (next-2) 1 (next-1) 4

Если в теле let определяется функция, то свободные переменные запоминаются в состоянии на тот Если в теле let определяется функция, то свободные переменные запоминаются в состоянии на тот момент, когда выполнялась defun. Эти переменные доступны в теле функции и могут модифицироваться. Функциональный объект Лиспа, включающий функцию и зафиксированные значения ее свободных переменных называется замыканием.

Чем неудобны наши функции Next-1 и Next-2? Тем, что они “шагают” только вперед. . Чем неудобны наши функции Next-1 и Next-2? Тем, что они “шагают” только вперед. . . Нельзя ли сделать возможность “сброса” значения внутренней переменной с? Что будет, если мы просто введем: (setq c 0) ? да ровным счетом ничего – будет создана глобальная переменная с, не имеющая отношения к Next-1 и Next-2.

Исправление неудобства: (let ((c 0)) (defun next-3 nil (setq c (+ 1 c))) (defun Исправление неудобства: (let ((c 0)) (defun next-3 nil (setq c (+ 1 c))) (defun reset-3 nil (setq c 0)) ) Теперь к переменной c имеют совместный доступ две функции. Вызов next-3 возвращает следующее значение, вызов reset-3 сбрасывает счетчик.

(next-3) 1 (next-3) 2 (next-3) 3 (reset-3) ; ; сброс счетчика 0 (next-3) ; (next-3) 1 (next-3) 2 (next-3) 3 (reset-3) ; ; сброс счетчика 0 (next-3) ; ; счет с начала. . . 1

Давайте вернемся к безымянным функциям и попробуем ввести лямбда-выражение, не указав параметры: (lambda (x) Давайте вернемся к безымянным функциям и попробуем ввести лямбда-выражение, не указав параметры: (lambda (x) (* x x)) (CLOSURE (x) ((* x x)) NIL) Это тоже замыкание, но, поскольку свободных переменных в теле лямбдавыражения нет, список переменных пуст.

Другой способ создания замыкания – конструкция FUNCTION: Вызов: (function Имя_ф-ции или лямбда-выражение) порождает замыкание, Другой способ создания замыкания – конструкция FUNCTION: Вызов: (function Имя_ф-ции или лямбда-выражение) порождает замыкание, в которой сохраняются все переменные, свободные относительно тела функции или лямбда-выражения.

Рассмотрим функцию: (defun make-adder (x) (function (lambda (y) (+ x y)))) make-adder (setq 3+ Рассмотрим функцию: (defun make-adder (x) (function (lambda (y) (+ x y)))) make-adder (setq 3+ (make-adder 3)) (CLOSURE (y) ((+ x y)) ((x 3))) Переменная x свободна относительно тела лямбда-выражения. Текущее значение x зафиксировано в замыкании.

Как вызвать функцию, с которой связана переменная 3+ ? Через funcall ! (funcall 3+ Как вызвать функцию, с которой связана переменная 3+ ? Через funcall ! (funcall 3+ 6) 9

Поскольку конструкция function используется достаточно часто, удобно использовать краткую запись: (function НЕЧТО) то же Поскольку конструкция function используется достаточно часто, удобно использовать краткую запись: (function НЕЧТО) то же самое, что: #’НЕЧТО

Итак, мы рассмотрели конструкции Лиспа, позволяющие использовать функции, как аргументы других функций и возвращать Итак, мы рассмотрели конструкции Лиспа, позволяющие использовать функции, как аргументы других функций и возвращать функции, как значения.

Две простенькие задачи 1) Дан список троек чисел вида: ((a b c) (d e Две простенькие задачи 1) Дан список троек чисел вида: ((a b c) (d e f). . . ). Построить список сумм троек (a+b+c d+e+f. . . ) 2) Напишите генератор, возвращающий при очередном вызове число Фибоначчи.