Определение функций — это только половина дела. Функции ничего не делают, пока вы их не вызовете, то есть не укажете им выполниться. Вызов функции также называется инвокацией.
Самый распространённый способ вызова функции — вычисление списка.
Например, вычисление списка (concat "a" "b") вызывает функцию
concat с аргументами "a" и "b".
См. Вычисление для описания вычисления.
Когда вы записываете список как выражение в своей программе, вы указываете,
какую функцию вызвать и сколько аргументов ей передать, в тексте программы.
Обычно это именно то, что вам нужно. Иногда требуется вычислить во время
выполнения, какую функцию вызывать. Для этого используется функция
funcall. Когда также нужно определить во время выполнения, сколько
аргументов передавать, используется apply.
funcall вызывает function с arguments и возвращает
то, что возвращает function.
Поскольку funcall является функцией, все её аргументы, включая
function, вычисляются до вызова funcall. Это означает,
что вы можете использовать любое выражение для получения вызываемой функции.
Это также означает, что funcall не видит выражения, которые вы
записали для arguments, а только их значения. Эти значения не
вычисляются повторно при вызове function; действие funcall
подобно обычной процедуре вызова функции, после того как её аргументы уже
были вычислены.
Аргумент function должен быть либо функцией Lisp, либо примитивной
функцией. Специальные формы и макросы не допускаются, поскольку они имеют
смысл только при передаче невычисленных выражений аргументов.
funcall не может их предоставить, потому что, как мы видели выше,
он их никогда не знает.
Если вам нужно использовать funcall для вызова команды и сделать так,
чтобы она вела себя как при интерактивном вызове, используйте
funcall-interactively (см. Интерактивный вызов).
(setq f 'list)
⇒ list
(funcall f 'x 'y 'z)
⇒ (x y z)
(funcall f 'x 'y '(z))
⇒ (x y (z))
(funcall 'and t nil) error→ Invalid function: #<subr and>
Сравните эти примеры с примерами для apply.
apply вызывает function с arguments, так же как
funcall, но с одним отличием: последний из arguments является
списком объектов, которые передаются function как отдельные
аргументы, а не как единый список. Говорят, что apply
разворачивает этот список так, что каждый отдельный элемент становится
аргументом.
apply с одним аргументом является особым случаем: первый элемент
аргумента, который должен быть непустым списком, вызывается как функция
с остальными элементами как отдельными аргументами. Передача двух или более
аргументов будет быстрее.
apply возвращает результат вызова function. Как и в случае
funcall, function должна быть либо функцией Lisp, либо
примитивной функцией; специальные формы и макросы не имеют смысла в
apply.
(setq f 'list)
⇒ list
(apply f 'x 'y 'z) error→ Wrong type argument: listp, z
(apply '+ 1 2 '(3 4))
⇒ 10
(apply '+ '(1 2 3 4))
⇒ 10
(apply 'append '((a b c) nil (x y z) nil))
⇒ (a b c x y z)
(apply '(+ 3 4))
⇒ 7
Интересный пример использования apply см. в Определение mapcar.
Иногда полезно зафиксировать некоторые аргументы функции на определённых значениях, а остальные аргументы оставить для момента фактического вызова функции. Действие фиксации некоторых аргументов функции называется частичным применением функции14. Результатом является новая функция, которая принимает остальные аргументы и вызывает исходную функцию со всеми объединёнными аргументами.
Вот как выполняется частичное применение в Emacs Lisp:
Эта функция возвращает новую функцию, которая при вызове будет вызывать
func со списком аргументов, составленным из args и
дополнительных аргументов, указанных во время вызова. Если func
принимает n аргументов, то вызов apply-partially с
m <= n аргументами создаст новую функцию от
n - m аргументов15.
Вот как можно было бы определить встроенную функцию 1+, если бы она
не существовала, используя apply-partially и +, другую
встроенную функцию16:
(defalias '1+ (apply-partially '+ 1) "Увеличить аргумент на единицу.")
(1+ 10)
⇒ 11
В Lisp часто встречается, что функции принимают функции в качестве аргументов
или находят их в структурах данных (особенно в переменных хуков и списках
свойств) и вызывают их с помощью funcall или apply. Функции,
которые принимают функциональные аргументы, часто называют функционалами.
Иногда при вызове функционала полезно передать в качестве аргумента функцию-заглушку. Вот три различных вида функций-заглушек:
Эта функция возвращает argument и не имеет побочных эффектов.
Эта функция игнорирует любые arguments и возвращает nil.
Эта функция игнорирует любые arguments и возвращает t.
Некоторые функции являются видимыми для пользователя командами,
которые могут вызываться интерактивно (обычно по последовательности клавиш).
Возможно вызвать такую команду точно так, как если бы она была вызвана
интерактивно, с помощью функции call-interactively.
См. Интерактивный вызов.
Это связано с каррированием, но отличается от него, которое преобразует функцию, принимающую несколько аргументов, таким образом, что её можно вызывать как цепочку функций, каждая из которых принимает один аргумент.
Если количество аргументов, которое может принимать func,
неограниченно, то новая функция также будет принимать неограниченное
количество аргументов, так что в этом случае apply-partially не
уменьшает количество аргументов, которое может принимать новая функция.
Обратите внимание, что в отличие от встроенной функции, эта версия принимает любое количество аргументов.