Зачем нам нужна funcall в Lisp?
Почему мы должны использовать funcall
для вызова функций более высокого порядка в Common Lisp? Например, почему мы должны использовать:
(defun foo (test-func args)
(funcall test-func args))
вместо простого:
(defun bar (test-func args)
(test-func args))
Исходя из процедурного фона, я немного удивлен тем, что, поскольку языки, к которым я больше привык (например, Python, С#), не нуждаются в различии. В частности, на исходном уровне, по крайней мере, компилятор С# преобразует его в нечто вроде func.invoke()
.
Единственная проблема, я вижу, это означает, что мы не могли бы назвать глобальную функцию test-func
больше, потому что она будет затенена, но это вряд ли проблема.
Ответы
Ответ 1
Строго говоря, funcall
не понадобится, но есть некоторые lisps (реализации List-2, такие как Common Lisp), которые разделяют пространство имен переменных пространства имен функций. Реализации List-1 (например, схема) не делают этого различия.
В частности, в вашем случае test-func
находится в пространстве имен переменных.
(defun foo (test-func args)
(funcall test-func args))
Поэтому вам нужна конструкция, которая фактически ищет объект функции, связанный с этой переменной, в пространстве имен переменных. В Common Lisp эта конструкция funcall
.
См. также этот ответ.
Ответ 2
Большинство Lisps имеют два пространства имен (функции и переменные). Имя просматривается в пространстве имен функций, когда оно появляется как первый элемент в S-выражении, а в противном случае - в пространстве имен переменных. Это позволяет вам называть ваши переменные, не беспокоясь о том, являются ли они теневыми функциями: поэтому вы можете назвать свою переменную list
вместо того, чтобы ее использовать в lst
.
Однако это означает, что когда вы храните функцию в переменной, вы не можете ее нормально вызвать:
(setq list #'+) ; updates list in the variable namespace
(list 1 2 3) => (1 2 3) ; looks up list in the function namespace
Следовательно, необходимо funcall
и apply
:
(funcall list 1 2 3) => 6 ; looks up list in the variable namespace
(Не все Lisps имеют два пространства имен: Схема является примером Lisp только с одним пространством имен.)
Ответ 3
Обратите внимание, что в любом Lisp, если вы хотите вызвать функцию каким-либо образом, кроме
(function fixed arg u ments)
вы должны использовать что-то в первой позиции формы, отличной от функции в любом случае. В некоторых диалектах, если F
- это переменная, которая содержит функцию, вы можете просто сделать это:
(f a b c) ;; no funcall
Но в почти всех диалектах вы не можете удалить apply
(apply f args-list) ;; Common Lisp or Scheme: same
Если вы столкнулись с funcall
, являющимся досадой, вы просто не используете достаточно интересных аппликаторов над своими функциональными аргументами.:)
Ответ 4
В Common Lisp каждый символ может быть связан с symbol-function и его symbol-value, между прочим. При чтении списка по умолчанию Common Lisp интерпретирует:
- arg1 как функция и поэтому извлекает
test-func
symbol-function
, который равен undefined - поэтому функция bar
не работает
- arg2 как что-то быть
eval
ed - таким образом, функция foo
извлекает test-func
symbol-value
, что в вашем случае оказывается функцией