Лисп/Функции: различия между версиями
Ramir (обсуждение | вклад) Нет описания правки |
Ramir (обсуждение | вклад) Нет описания правки |
||
Строка 1: | Строка 1: | ||
<div style="max-width:60em;margin:1em auto 0 4%;"> |
<div style="max-width:60em;margin:1em auto 0 4%;"> |
||
Если вы уже знакомы с каким-нибудь алголоподобным языком типа [[Категория:Си|Си]] или Пёрла, то понятие '''функции''' в Лиспе может показаться странным, но оно значительно ближе к математическому явлению функции, чем «функции» Си, которые вернее бы назвать более процедурами. Функция в Лиспе есть однозначное отображение множества исходных данных на множество её значений. У функции может быть произвольно много аргументов, — от нуля до <s>бесконечности</s> любого конечного числа, — но обязательно должно быть хотя бы одно значение. Обычная для многих языков префиксная запись вызова функций: |
Если вы уже знакомы с каким-нибудь алголоподобным языком типа [[:Категория:Си|Си]] или Пёрла, то понятие '''функции''' в Лиспе может показаться странным, но оно значительно ближе к математическому явлению функции, чем «функции» Си, которые вернее бы назвать более процедурами. Функция в Лиспе есть однозначное отображение множества исходных данных на множество её значений. У функции может быть произвольно много аргументов, — от нуля до <s>бесконечности</s> любого конечного числа, — но обязательно должно быть хотя бы одно значение. Обычная для многих языков префиксная запись вызова функций: |
||
<source lang=c>plus(2,2)</source> |
<source lang=c>plus(2,2)</source> |
||
несколько видоизменяется в силу отсутствия синтаксиса: идентификатор ("имя" функции, спецоператора или [[Лисп/Макросы|макровызова]]) становится головным элементом… да, списка: |
несколько видоизменяется в силу отсутствия синтаксиса: идентификатор ("имя" функции, спецоператора или [[Лисп/Макросы|макровызова]]) становится головным элементом… да, списка: |
||
Строка 52: | Строка 52: | ||
</source> |
</source> |
||
В последнем случае квотируется не только <code>сумма-квадратов</code>, но и сам eval! |
В последнем случае квотируется не только <code>сумма-квадратов</code>, но и сам eval! |
||
[[Категория: |
[[Категория:Язык Си в примерах]] |
Версия от 01:11, 30 декабря 2008
Если вы уже знакомы с каким-нибудь алголоподобным языком типа Си или Пёрла, то понятие функции в Лиспе может показаться странным, но оно значительно ближе к математическому явлению функции, чем «функции» Си, которые вернее бы назвать более процедурами. Функция в Лиспе есть однозначное отображение множества исходных данных на множество её значений. У функции может быть произвольно много аргументов, — от нуля до бесконечности любого конечного числа, — но обязательно должно быть хотя бы одно значение. Обычная для многих языков префиксная запись вызова функций:
plus(2,2)
несколько видоизменяется в силу отсутствия синтаксиса: идентификатор ("имя" функции, спецоператора или макровызова) становится головным элементом… да, списка:
(plus 2 2)
В чистом функциональном лиспе[1] функции не должны обладать побочным эффектом, то есть изменять значения переменных с нелокальным состоянием. В противном случае, при нескольких вызовах одной функции могут быть получены разные значения, что запутывает любую сложную программную систему, и чего стремится избежать функциональное_программирование.
Определение функции основано на лямда-исчислении. Исходный вариант записи лямда-выражения, предложенный его автором Чёрчем, выглядит как
. Лисп-запись выглядит так: (lambda (x1 x2 ...) fn)
. x1, x2 ... - свободные параметры, и, так, могут быть безболезненно заменены на другие без изменении значения лямбда-выражения.
Например, сумму квадратов на языке лямбда-выражений можно определить как (lambda (x y) (+ (* x x) (* y y)))
. Вызов лямбда-функции с конкретными значениями ((lambda (x y) (+ (* x x) (* y y))) 2 3) => (+ (* 2 2) (* 3 3)) => 13
может быть сделан единожды, при желании вызвать такую функцию еще раз необходимо повторить запись еще раз. Это неудобно. Было бы удобнее написать нечто типа (сумма-квадратов 2 3). Это возможно, если вы предварительно определите функцию сумма-квадратов.
(defun сумма-квадратов (x y)
((lambda (a b) (+ (* a a) (* b b)))
x y))
…но не пугайтесь: для удбства эта запись может быть сокращена до
(defun сумма-квадратов (x y)
(+ (* x x) (* y y)))
Одно из преимуществ столь непривычных особенностей языка Лисп в том, что вы можете написать нечто типа
((lambda (x)
((lambda (y)
(list x y))
'second))
'first)
Здесь лямда-выражение является телом другого лямда-выражения. Оно так же может быть одним из его аргументов.
Посмотреть ранее заданное определение функции можно с помощью функции symbol-function
:
>> (symbol-function 'сумма-квадратов)
<< #<function сумма-квадратов (x y) (declare (system::in-defun сумма-квадратов))
(block сумма-квадратов (+ (* x x) (* y y)))>
Штрих перед названием функции поставлен неслучайно. В Лиспе любое имя соответствует некоторому символу. Имя функции - не исключение, то есть значение символа «сумма-квадратов
» - не определение функции; оно неопределено, пока не будет присвоено вручную (например, через setq
.) Штрих - это сокращенная запись спецоператора quote
, сообщающая интерпретатору, что нужно вывести имя данного символа без вычисления его значения.
>> (symbol-function сумма-квадратов) ; это выдаст ошибку: переменной сумма-квадратов не присвоено значение
>> (symbol-function (quote сумма-квадратов)) ; это правильно, но долго
>> (symbol-function 'сумма-квадратов) ; так лучше всего
Если все аргументы лямбда-выражения не должны вычисляться, то удобно использовать nlambda
вместо lambda
.
quote
’е противопоставлен eval
, принудительно вычисляющий значение выражения.
>> (quote сумма-квадратов) => сумма-квадратов
>> (eval (quote сумма-квадратов)) => ошибка!
>> (quote (eval (quote сумма-квадратов))) => (eval 'sum)
В последнем случае квотируется не только сумма-квадратов
, но и сам eval!
- ↑ Являющемся строгим подмножеством любого из диалектов