2012-03-15 4 views
14

Почему мы должны использовать funcall для вызова функций более высокого порядка в Common Lisp? Например, почему мы должны использовать:Зачем нам нужен funcall в Lisp?

(defun foo (test-func args) 
    (funcall test-func args)) 

вместо простого:

(defun bar (test-func args) 
    (test-func args)) 

Coming из процедурного фона, я немного удивлен тем, что с языками я больше используемый (например, Python, C#), не требует различия. В частности, на исходном уровне, по крайней мере, компилятор C# преобразует его в нечто вроде func.invoke().

Единственная проблема, которую я вижу, это то, что это означало бы, что мы больше не могли бы называть глобальную функцию test-func, потому что она была бы затенена, но это вряд ли проблема.

+3

«Вряд ли проблема» - суть проблемы. На практике мне не сложно вызвать funcall и было бы неудобно, если бы мне пришлось выбирать творческие/странные имена для переменных, чтобы избежать столкновений с CAR, LIST, REST и т. Д. – Xach

ответ

16

Строго говоря, funcall был бы не нужен, но есть некоторые лепечет (список 2-реализации, такие как Common Lisp), отделяющие имени переменного пространство в функции пространства имен. Реализации List-1 (например, схема) не делают этого различия.

В частности, в вашем случае test-func находится в пространстве имен переменных.

(defun foo (test-func args) 
    (funcall test-func args)) 

Поэтому вам нужна конструкция, которая фактически ищет объект функции, связанный с этой переменной, в пространстве имен переменных. В Common Lisp эта конструкция - funcall.

См. Также this answer.

12

Большинство 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 

(Не все Lisp имеют два пространства имен: схема является примером Lisp с одним пространством имен.)

+0

Да, преимущество в том, что функции слежения не являются хорошими, но, с другой стороны, мы получаем специальные случаи для функций .. ну, я полагаю, так как нам не нужно беспокоиться о позиционировании скобок в lisp, нам нужны другие религиозные войны;) Поскольку вы и Диего оба ответили на мой вопрос красиво, я пошел со временем в качестве тай-брейкера и принял ответ Диего. – Voo

-2

В Common Lisp каждый символ может быть связан с его symbol-function и его symbol-value, между прочим.При чтении списка, по умолчанию, Common Lisp интерпретирует:

  • arg1 как функция и так получает «s symbol-function, который не определен test-func - таким образом, функция bar не работает
  • arg2 как-то быть eval редактор - таким образом, функция foo извлекает test-func «s symbol-value, который, в вашем случае, случается, функция
+0

Это не то, что делает реализация Common Lisp. Вы можете прочитать лексические привязки. –

1

Обратите внимание, что в любой Lisp, если вы хотите, чтобы вызвать функцию в некотором роде, кроме

(function fixed arg u ments) 

Вы все равно должны использовать что-то в первой позиции формы, отличной от функции. В некоторых диалектах, если F это переменная, которая имеет функцию, вы можете просто сделать это:

(f a b c) ;; no funcall 

Но в почти всех диалектах, вы не можете удалить apply

(apply f args-list) ;; Common Lisp or Scheme: same 

Если вы «перебегая в funcall, являясь неприятностью, вы просто не используете достаточно интересных аппликаторов над своими функциональными аргументами. :)

Смежные вопросы