Это хороший вопрос, и тот, в котором Common Lisp может быть довольно запутанным.Дело в том, что по историческим причинам Common Lisp имеет два пространства имен - одно для функций, а другое для значений.Чтобы это произошло, есть два разных правила оценки для положения головы приложения-функции и для остальных: первое оценивает символ как имя функции, а второе оценивает символ как ссылку на переменную.Очевидно, есть случаи, когда значение на самом деле является функцией - например, если вы пишете функцию mapcar
, вам нужно будет выполнить что-то вроде
(defun my-mapcar (f l)
(if (null l)
'()
(cons (f (car l)) (my-mapcar f (cdr l)))))
, но это не сработает -он будет жаловаться на то, что f
является неизвестной функцией.Для этих случаев есть специальная функция с именем funcall
, которая получает функцию и аргумент для функции и будет применять функцию как обычно - а поскольку funcall
- простая функция, все ее аргументы оцениваются как обычно (как значения).Таким образом, вышеприведенное должно быть исправлено с помощью этого:
(defun my-mapcar (f l)
(if (null l)
'()
(cons (funcall f (car l)) (my-mapcar f (cdr l)))))
Как вы, вероятно, подозреваете сейчас, есть зеркальные случаи - когда вы хотите оценить что-то как функцию, а не ценность.Например, это не работает:
(my-mapcar 1+ '(1 2 3))
, потому что это относится к переменной 1+
, а не к функции.Для этих случаев есть специальная форма с именем function
, которая оценивает ее содержимое как функцию и возвращает его как значение:
(my-mapcar (function 1+) '(1 2 3))
, и ее можно сократить #'
:
(my-mapcar #'1+ '(1 2 3))
Это не конец этой истории - привести несколько примеров:
в некоторых случаях простое имя в кавычках может работать как функция - например, '1+
в последнем примере работает - но это своего рода хак, который может видеть только глобально связанные имена, поэтому #'
почти всегда лучше
аналогичный хак можно использовать сlambda
- так что вы можете использовать (my-mapcar '(lambda (x) (1+ x)) '(1 2 3))
, фактически вы можете использовать (list 'lambda '(x) '(1+ x))
, что еще хуже (и IIRC, непереносимое), но использование (lambda (x) (1+ x))
работает, поскольку оно неявно заключено в #'
(попробуйте развернуть форму lambda
как макрос, и вы увидите ее).Связанный хак позволяет использовать выражение lambda
в качестве заголовка приложения-функции (что вы и пытались).
let
и т.д. bindлокальные значения, и в некоторых случаях вам нужно вместо этого связывать локальные функции - для этого есть новые конструкции привязки: flet
и labels
Если все этовыглядит странно и / или слишком сложно, тогда вы не одиноки.Это одно из главных отличий между Common Lisp и Scheme.(Затем различие приводит к изменениям в общих идиомах в обоих языках: код схемы имеет тенденцию использовать функции более высокого порядка гораздо чаще, чем код Common Lisp. Как обычно, с такими религиозными вопросами, некоторые люди спорят в пользу того, что делает CL, утверждая, чточто функции высшего порядка сбивают с толку, поэтому им нравится явное напоминание в коде.)