Использование значения лямбда из функции в качестве первого элемента списка

StackOverflow https://stackoverflow.com/questions/5850553

Вопрос

Я читаю парадигмы программ искусственного интеллекта Питера Норвига, и я сталкивался с проблемой, которую я не могу решить самостоятельно (это мое знакомство с 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-функции тоже не помогут здесь).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top