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
(но я не думаю, что это то, что вы пытались сделать)