Зачем нам funcall в Лиспе?
Почему мы должны использовать funcall
вызывать функции высшего порядка в Common Lisp? Например, почему мы должны использовать:
(defun foo (test-func args)
(funcall test-func args))
вместо более простого:
(defun bar (test-func args)
(test-func args))
Исходя из процедурного фона, я немного удивлен тем, что языки, к которым я более привык (например, Python, C#), не нуждаются в различении. В частности, по крайней мере на исходном уровне, компилятор C# преобразует его в нечто вроде func.invoke()
,
Единственная проблема, которую я вижу, состоит в том, что это означает, что мы не можем вызвать глобальную функцию test-func
больше, потому что это будет затенено, но это вряд ли проблема.
4 ответа
Строго говоря, funcall
в этом нет необходимости, но есть некоторые списки (реализации list-2, такие как Common Lisp), которые разделяют пространство имен переменных пространства имен функций. Реализации List-1 (например, Scheme) не делают этого различия.
Более конкретно, в вашем случае, test-func
находится в пространстве имен переменных.
(defun foo (test-func args)
(funcall test-func args))
Поэтому вам нужна конструкция, которая фактически ищет объект функции, связанный с этой переменной, в пространстве имен переменных. В Common Lisp эта конструкция funcall
,
Смотрите также этот ответ.
У большинства Лиспов есть два пространства имен (функции и переменные). Имя ищется в пространстве имен функции, когда оно появляется как первый элемент в 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
(Не все Лиспы имеют два пространства имен: Схема является примером Лиспа только с одним пространством имен.)
Обратите внимание, что в любом Лиспе, если вы хотите вызвать функцию, отличную от
(function fixed arg u ments)
в любом случае вы должны использовать что-то в первой позиции формы, кроме функции. На некоторых диалектах, если F
переменная, которая содержит функцию, вы можете просто сделать это:
(f a b c) ;; no funcall
Но почти во всех диалектах вы не можете удалить apply
(apply f args-list) ;; Common Lisp or Scheme: same
Если вы столкнулись с funcall
неудобство, вы просто не используете достаточно интересных аппликаторов для своих функциональных аргументов.:)
В Common Lisp каждый символ может быть связан, помимо прочего, со своей символьной функцией и его символьным значением. При чтении списка по умолчанию Common Lisp интерпретирует:
- arg1 как функция и так извлекает
test-func
"ssymbol-function
, который не определен - таким образом, функцияbar
не работает - arg2 как то быть
eval
эд - таким образом функцияfoo
извлекаетtest-func
"ssymbol-value
, который, в вашем случае, оказывается функцией