Зачем нам 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"s symbol-function, который не определен - таким образом, функция bar не работает
  • arg2 как то быть evalэд - таким образом функция foo извлекает test-func"s symbol-value, который, в вашем случае, оказывается функцией
Другие вопросы по тегам