Зачем нам нужна 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, что в вашем случае оказывается функцией