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

(но я не думаю, что это то, что вы пытались сделать)