Как мне делать замыкания в Emacs Lisp?

Я пытаюсь на лету создать функцию, которая возвращала бы одно постоянное значение.

В JavaScript и других современных императивных языках я бы использовал замыкания:

function id(a) {
    return function() {return a;};
}

но Emacs lisp их не поддерживает.

Я могу создать сочетание функции идентификации и приложения частичной функции, но это тоже не поддерживается.

Так как мне это сделать?

Ответов (7)

Решение

Глупая идея: как насчет:

(defun foo (x)
  `(lambda () ,x))

(funcall (foo 10))  ;; => 10

Emacs 24 имеет лексическую привязку.

http://www.emacswiki.org/emacs/LexicalBinding

Настоящие (не поддельные) замыкания в Emacs 24.

Хотя в Emacs 24 есть лексическое копирование, когда переменная lexical-binding имеет значение t , специальная форма defun не работает должным образом в лексически связанных контекстах (по крайней мере, не в Emacs 24.2.1). Это затрудняет, но не делает невозможным, определить настоящие (не поддельные) замыкания. Например:

(let ((counter 0))
   (defun counting ()
    (setq counter (1+ counter))))

не будет работать должным образом, потому что счетчик символов в defun будет привязан к глобальной переменной этого имени, если она есть, а не к лексической переменной, определенной в let . Когда вызывается функция подсчета , если глобальная переменная не существует, она, очевидно, завершится ошибкой. Однако, если существует такая глобальная переменная, она должна быть обновлена, что, вероятно, не является тем, что было задумано, и может быть трудно отследить ошибку, поскольку функция может работать должным образом.

Компилятор байтов выдает предупреждение, если вы используете defun таким образом, и предположительно проблема будет решена в какой-нибудь будущей версии Emacs, но до тех пор можно использовать следующий макрос:

(defmacro defun** (name args &rest body)
  "Define NAME as a function in a lexically bound context.

Like normal `defun', except that it works correctly in lexically
bound contexts.

\(fn NAME ARGLIST [DOCSTRING] BODY...)"
  (let ((bound-as-var (boundp  `,name)))
    (when (fboundp `,name)
      (message "Redefining function/macro: %s" `,name))
    (append
     `(progn
        (defvar ,name nil)
        (fset (quote ,name) (lambda (,@args) ,@body)))
     (if bound-as-var
         'nil
         `((makunbound `,name))))))

Если вы определяете подсчет следующим образом:

(let ((counter 0))
  (defun** counting ()
    (setq counter (1+ counter))))

он будет работать, как ожидалось, и обновлять лексически привязанный счетчик переменных каждый раз, когда он вызывается, при этом возвращая новое значение.

ПРЕДОСТЕРЕЖЕНИЕ: макрос не будет работать должным образом, если вы попытаетесь отключить ** функцию с тем же именем, что и одна из лексически связанных переменных. Т.е. если вы сделаете что-то вроде:

(let ((dont-do-this 10))
  (defun** dont-do-this ()
    .........
    .........))

Я не могу представить, чтобы кто-то на самом деле делал это, но об этом стоило упомянуть.

Примечание. Я назвал макрос defun **, чтобы он не конфликтовал с макросом defun * в пакете cl , однако он никоим образом не зависит от этого пакета.

;; -*- lexical-binding:t -*-

(defun create-counter ()
  (let ((c 0))
    (lambda ()
      (setq c (+ c 1))
      c)))

(setq counter (create-counter))

(funcall counter) ; => 1
(funcall counter) ; => 2
(funcall counter) ; => 3 ...

Emacs lisp имеет только динамическую область видимости. Есть lexical-let макрос, который приближает лексическую область видимости с помощью довольно ужасного взлома.

Нашел другое решение с lexical-let

(defun foo (n) 
    (lexical-let ((n n)) #'(lambda() n)))

(funcall (foo 10)) ;; => 10

http://www.emacswiki.org/emacs/FakeClosures