Определение функций

Лямбда-выражение изображает параметризованные вычисления

  Определение функций и их вычисление в Лиспе основано на лямбда-исчислении Чёрча, предлагающем для этого точный и простой формализм. Лямбда-выражение, позаимствованное Лиспом из лямбда-исчисления является важным механизмом в практическом программировании.
  В лямбда-исчислении Чёрча функция записывается в следующем виде:

  lambda(x1,x2,…,xn).fn

  В Лиспе лямбда-выражение имеет вид:

  (lambda (x1 x2 … xn) {fn})

  Символ lambda означает, что мы имеем дело с определением функции. Символы xi являются формальными параметрами определения, которые именуют аргументы в описывающем вычисления теле функции {fn}. Входящий в состав формы список, образованный из параметров, называют лямбда-списком.
  Телом функции является произвольная форма, значение которой может вычислить интерпретатор Лиспа, например: константа, связанный со значением символ или композиция из вызовов функций. Функцию, вычисляющую сумму квадратов двух чисел можно, например, определить следующим лямбда-выражением:

(lambda (x  y)  ((x * x)  + (y  * y))

  Формальность параметров означает, что их можно заменить на любые другие символы, и это не отразится на вычислениях, определяемых функцией. Именно это и скрывается за лямбда-нотацией. С её помощью возможно различать понятия определения и вызова функции. Например, функцию list для двух аргументов можно определить любым из двух лямбда-выражений:

(lambda (x  y)  (nil  cons  x (nil  cons  y nil)))
(lambda (кот пёс) (nil  cons  кот  (nil  cons  пёс  nil)))

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

Лямбда-вызов соответствует вызову функции

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

  (obj лямбда-выражение a1 a2 … an)

  Здесь ai - формы, задающие фактические параметры, которые вычисляются как обычно. Например, действие сложения для чисел 2 и 3

>(2 + 3)
5

можно записать с использованием вызова лямбда-выражения:

>(2 ; объект
  (lambda (x) (this + x)) ; лямбда-определение
  3)  ; аргумент
5 ; результат

  Следующий вызов строит список из аргументов a и b:

>(nil
  (lambda (x  y)  (nil  cons  x (nil  cons  y nil)))
  'a  'b)
(a  b)

  Такую форму вызова называют лямбда-вызовом.

Вычисление лямбда-вызова, или лямбда-преобразование

  Вычисление лямбда-вызова, или применение лямбда-выражения к фактическим параметрам, производится в два этапа. Сначала вычисляются значения фактических параметров и соответствующие формальные параметры связываются с полученными значениями. Этот этап называется связыванием параметров. На следующем этапе с учётом новых связей вычисляется форма, являющаяся телом лямбда-выражения, и полученное значение возвращается в качестве значения лямбда-вызова. Формальным параметрам после окончания вычисления возвращаются те связи, которые у них, возможно, были перед вычислением лямбда-вызова. Весь этот процесс называют лямбда-преобразованием.

Объединение лямбда-вызовов

  Лямбда-вызовы можно свободно объединять между собой и другими формами. Вложенные лямбда-вызовы можно ставить как на место тела лямбда-выражения, так и на место фактических параметров. Например, в следующем вызове тело лямбда-выражения содержит вложенный лямбда-вызов:

>('внешний
  (lambda ()  ; у лямбда-вызова тело вновь лямбда-вызов
    ('внутренний  (lambda (x) (nil  list  this  x)) this)))
(внутренний внешний)

  В приведённом ниже примере лямбда-вызов является аргументом другого вызова:

>(('первый
  (lambda ()  ; лямбда-вызов у которого аргументом является новый лямбда-вызов
    (nil  list  this)))
  (lambda ()  (nil  list  'второй this)))
(второй (первый))

  Обратите внимание, что лямбда-выражение без аргументов (фактических параметров) представляет собой лишь определение, но не форму, которую можно вычислить. Само по себе оно интерпретатором не воспринимается:

>(lambda  (x  y)  (nil  cons  x (nil  cons  y nil)))
Error:eval: Symbol lambda have no value

  Символ lambda не имеет значения

Лямбда-выражение - функция без имени

  Лямбда-выражение является как чисто абстрактным механизмом для определения и описания вычислений, дающим точный формализм для параметризации вычислений при помощи переменных и изображения вычислений, так и механизмом для связывания формальных и фактических параметров на время выполнения вычислений. Лямбда-выражение - это безымянная функция, которая пропадает тотчас после вычисления значения формы. Её трудно использовать снова, так как нельзя вызвать по имени, хотя ранее выражение было доступно как списочный объект. Однако используются и безымянные функции, например при передаче функции в качестве аргумента другой функции или при формировании функции в результате вычислений, другими словами, при синтезе программ.

DEFMETHOD даёт имя описанию метода

  Лямбда-выражение соответствует используемому в других языках определению процедуры или функции, а лямбда-вызов - вызову процедуры или функции. Различие состоит в том, что для программиста лямбда-выражение - это лишь механизм, и оно не содержит имени или чего-либо подобного, позволяющего сослаться на это выражение из других вызовов. Записывать вызовы функций полностью с помощью лямбда-вызовов не разумно, поскольку очень скоро выражения в вызове пришлось бы повторять, хотя разные вызовы одной функции отличаются лишь в части фактических параметров. Проблема разрешима если дать имя лямбда-выражению и использования в вызове лишь имени.
  Дать имя и определить новую функцию можно с помощью функции defmethod. Её действие с абстрактной точки зрения аналогично функциям (set и другие). defmethod вызывается так:

  (имя-класса defmethod имя-функции список-аргументов тело)

  Что можно представить себе как сокращение записи

  (имя-класса defmethod имя-функции лямбда-выражение)

из которой для удобства исключены внешние скобки лямбда-выражения и сам атом lambda. Например:

(nil  defmethod list1 (lambda (x  y)  (nil  cons  x (nil  cons  y nil))))

(nil  defmethod list1 (x  y)  (nil  cons  x (nil  cons  y nil)))

  defmethod соединяет символ с лямбда-выражением, и символ начинает представлять (именовать) определённые этим лямбда-выражением вычисления. Значением этой формы является лямбда-выражение новой функции:

>(nil defmethod list1 (x  y)  (nil  cons  x (nil  cons  y nil)))  ; определение
(lambda (x  y)  (nil  cons  x (nil  cons  y nil)))
>(nil list1 'a  'b) ; вызов
(a  b)

  Приведём ещё несколько примеров:

>(nil defmethod lambdap (выражение)  ; проверяет на лямбда-выражение
  (nil  if  (nil  consp выражение)
    (nil  eq  (выражение first)  'lambda)))
(lambda (выражение)  (nil  if  (nil  consp выражение) (nil  eq  (выражение first)  'lambda)))
>(nil lambdap '(nil list1 'a  'b))
false
>('number defmethod проценты  (часть)  ; вычисляет % части
  ((часть  / this) * 100))
(lambda (часть)  ((часть  / this) * 100))
>(25  проценты  4)
16

GETMETHOD выдаёт определение метода

  Ранее был рассмотрен предикат boundp, проверяющий наличие у символа значения. Соответственно функция getmethod возвращает определение метода, которую можно воспринимать как в логическом смысле, так и для взятия его определения.

>(nil getmethod 'list1)
(lambda (x  y)  (nil  cons  x (nil  cons  y nil)))

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

>(nil setq  list1 'a)
nil
>(nil list1 list1 'b)
(a  b)

  Символы могут дополнительно к значению и определению обладать в более общем виде и другими свойствами, определёнными пользователями, т.е. обладать списком свойств. Определение метода и значение переменных являются лишь двумя различными свойствами переменных, которые программист при необходимости может при помощи списка свойств дополнить или изменить.

Задание параметра в лямбда-списке

  Рассмотренной ранее defmethod-формы вполне достаточно для изучения Лиспа. Однако defmethod-форма содержит, кроме этого, механизм ключевых слов, с помощью которых аргументы вызова метода можно при желании трактовать по-разному.
  С помощью ключевого слова в лямбда-списке можно выделить параметр, связываемый с хвостом списка аргументов изменяющейся длины.
  Ключевые слова начинаются со знака &, и их записывают перед соответствующими параметрами в лямбда-списке.

&restПеременное количество аргументов

  Аргумент, указанный после ключевого слова &rest, связывается со списком несвязанных аргументов, указанных в вызове. Таким функциям можно передавать переменное количество аргументов.
  Например:

>(nil defmethod fn  (x  &rest y)
  (nil  list  x y))
(lambda (x  &rest y)  (nil  list  x y))
>(nil fn  'a)
(a  nil)
>(nil fn  'a  'b  'c)
(a  (b  c))

Функции вычисляют все аргументы

  В данной реализации нет механизма, с помощью которого можно было бы обозначать параметры, не требующие вычисления. Блокирующие вычисление аргумента метода и формы, такие как ', setq и defmethod, определяются через механизм макросов или макрооператоров.