Использование значения лямбда из функции в качестве первого элемента списка
-
27-10-2019 - |
Вопрос
Я читаю парадигмы программ искусственного интеллекта Питера Норвига, и я сталкивался с проблемой, которую я не могу решить самостоятельно (это мое знакомство с LISP). Проблема довольно маленькая, на самом деле, но, очевидно, не тот маленький мозг, который может решить.
Почему, когда значение функции является лямбдой, это ошибка использования этой функции в качестве первого элемента в списке. Например:
LISP:
(defun some-func ()
#'(lambda (x) x))
;; At REPL
;; Does not work
> ((some-func) 1)
;; Does work
> ((lambda (x) x) 1)
;; Also works
> (funcall (some-func) 1)
Надеюсь это имеет смысл!
Решение
Это хороший вопрос, и тот, где 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
и т. д. связывают локальные значения, и в некоторых случаях вы захотите связать локальные функции - для этого есть новые конструкции связывания:flet
а такжеlabels
Если все это выглядит странно и/или слишком сложным, то вы не одиноки. Это одно из основных различий между обычным LISP и схемой. (Разница тогда приводит к изменениям общих идиом на обоих языках: код схемы имеет тенденцию использовать функции более высокого порядка гораздо чаще, чем общий код LISP. Как обычно с такими религиозными вопросами, некоторые люди утверждают, что делает CL, утверждая что функции более высокого порядка сбивают с толку, поэтому им нравится явное напоминание в коде.)
Другие советы
((lambda (x) ...) ...)
является жестким специальным случаем в правилах оценщика. Он не оценивает первый элемент в форме и использует результат в качестве общего случая. Это нормально, что вам нужно использовать funcall
или же apply
Чтобы вызвать функцию, которая является результатом оценки какой -то другой формы.
Причина, по которой общий LISP не позволяет функции, которая возвращает лямбду быть первым элементом в выражении, связана с различием между LISP-1 и LISP-2.
Если разрешено обыкновенное LISP ((some-func) 1)
быть эквивалентным (funcall (some-func) 1)
, тогда это может быть воспринято как непоследовательное, чтобы не разрешать сказать (let ((f #'some-func)) (f 1))
а не требовать (funcall f 1)
.
На самом деле есть очень хорошее оправдание для того, чтобы не поддержать такие формы: они неоднозначны. Рассмотрим путь имена функций определены в общем LISP:
имя функции не. Анкет 1. (В Окружающая среда) А символ или список
(setf symbol)
это имя а функция в этом Окружающая среда. Анкет 2. символ или список(setf symbol)
.
Учитывая это, как должна вести себя такая форма, как следующая?
((setf car) 10 x)
Если это установлено x
Автомобиль к значению 10
, или он должен выполнить форму (setf car)
(Что было бы ошибкой) и попытаться использовать его возвращаемое значение в качестве функции для вызова с аргументами 10
а также x
? Common Lisp не указывает ни поведения, и мне совсем не ясно, что это плохой выбор. В конце концов, насколько я вижу, ничто в стандарте не предотвращает соответствующие реализации от расширения определения действительных форм для поддержки более широкого диапазона имен операторов (поэтому специальные функции SETF-функции тоже не помогут здесь).