Emacs lisp: почему этот sexp вызывает ошибку недопустимой функции?
в sexp в вопрос
(((lambda (b)
(lambda (a)
(+ b a))) 3) 5)
что, по-моему, похоже на то, что он должен оценить 8
, и в других шепелявит (например, ракетка) это делает, но в elisp он вместо этого бросает эту ошибку:
Debugger entered--Lisp error: (invalid-function ((lambda (b) (lambda (a) (+ b a))) 3))
похоже, он говорит мне, что
((lambda (b)
(lambda (a)
(+ b a))) 3)
не является допустимой функцией. Это кажется неправильным, потому что, когда я оцениваю это выражение я понимаю
(lambda (a) (+ b a))
который выглядит как действительная функция для меня. Кто-нибудь знает, почему это происходит? Делает это как-то связано с динамическим обзором?
4 ответов
здесь есть два вопроса. Первый-это вопрос синтаксиса, как указывали другие ответы. Второй вопрос, который упоминается в этом вопросе, касается вопросов охвата.
Синтаксический Вопрос
в Emacs Lisp (и других Lisps в Lisp-2 family), вызов функции выглядит как (f args...)
здесь f
является либо символом, который имеет значение функции, либо lambda
выражение. Например,
(list 1 2 3)
=> (1 2 3)
, потому что list
имеет привязку функции. Кроме того,
((lambda (x y) (list x x y y)) 1 2)
=> (1 1 2 2)
, потому что (lambda (x y) (list x x y y))
это lambda
выражение. Однако вы не можете использовать что-то, значение чего является функцией.
(let ((id (lambda (x) x)))
(id 3))
сигналы a Lisp error: (void-function id)
. Но мы можем вызывать значения функций с помощью funcall
:
(let ((id (lambda (x) x)))
(funcall id 3))
=> 3
Примечание: это достаточно хороший способ смотреть на это, но на самом деле все немного сложнее. См.9.2 Виды форм в руководстве для деталей и эзотерических битов, таких как функция indirection.
Итак, теперь мы можем решить проблему синтаксиса. Исходный код, немного переформатированный, чтобы указать, какие функции получают какие аргументы, является:
(((lambda (b)
(lambda (a)
(+ b a)))
3)
5)
как я понимаю, намерение состоит в том, чтобы сначала позвонить (lambda (b) ...)
С аргументом 3
чтобы вернуть анонимную функцию,(lambda (a) ...)
. В Emacs Lisp это будет:
((lambda (b)
(lambda (a)
(+ b a)))
3)
=> (lambda (a) (+ b a))
теперь, вы также хотите вызвать возвращенную анонимную функцию с помощью 5
. Мы используем funcall
для этого:
(funcall ((lambda (b)
(lambda (a)
(+ b a)))
3)
5)
К Вопросу Об Аналитическом Исследовании
к сожалению, этот код производит Lisp error: (void-variable b)
. этой здесь мы, наконец, сталкиваемся с проблемой динамического и лексического охвата. Потому что переменная b
был связан динамически, его значение не сохраняется в анонимной функции (lambda (a) (+ b a))
. Мы можем проверить, что это то, что происходит вокруг вся форма в чем-то, что связывает b
и видим, что происходит:
(let ((b 100))
(funcall ((lambda (b)
(lambda (a)
(+ b a)))
3)
5))
=> 105
я не очень хакер Emacs Lisp, поэтому я не уверен в лучшие способ получить лексические замыкания в Emacs. Я читал, что Emacs 24 имеет его, но я все еще на 23 здесь. На основе ответ, однако, мы можем использовать lexical-let
чтобы получить результаты, которые нам нужны:
(funcall ((lambda (b)
(lexical-let ((b b))
(lambda (a)
(+ b a))))
3)
5)
=> 8
lexical-let
устанавливает лексическую привязку, которая нам нужна, так что анонимная функция (lambda (a) ...)
это 3
застрял в нем. Более конкретно, мы ввели лексическое связывание b
, и именно эта лексическая привязка (lambda (a) …)
ссылки. На самом деле, если мы посмотрим на возвращенную анонимную функцию сейчас, это не просто (lambda (a) (+ b a))
, но печатается более сложным (и менее полезным) способом:
((lambda (b)
(lexical-let ((b b))
(lambda (a)
(+ b a))))
3)
=> (lambda (&rest --cl-rest--) (apply (lambda (G27322 a) (+ ... a)) (quote --b--) --cl-rest--))
в стороне, не имеет значения, что лексически связанная переменная b
имеет то же имя, что и динамически связанный b
; мы могли бы использовать (lexical-let ((c b)) ... (+ c a) ...)
, вместо.
короткий ответ: это эффект Lisp-1 против Lisp-2
немного более длинный ответ: для приложения Emacs Lisp вызывает значение функции первого символа в списке, но lambda
возвращает простой объект функции. Это означает, что вам придется использовать funcall
чтобы все работало. funcall
применяет свой второй аргумент к остальным аргументам (например,apply
, но последний expexts его последний аргумент должен быть списком).
(funcall (lambda (a) (funcall (lambda (b) (+ a b)) 5)) 3)
=> 8
принять посмотрите документацию emacs (например,C-h f funcall
) для получения дополнительной информации.
на car
of (((lambda (b) (lambda (a) (+ b a))) 3) 5)
не является ни символом с допустимым определением функции, ни символом lambda
, поэтому это недопустимый вызов функции.
FWIW, другая перестановка даст вам это:
((lambda (b)
((lambda (a)
(+ b a))
3))
5)
=> 8
(но я не думаю, что это то, что вы пытались сделать)