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

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

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

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

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

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

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

7 ответов

Решение

Глупая идея

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

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

Нашел другое решение с лексической-лет

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

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

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

Хотя Emacs 24 имеет лексическую черпаемость, когда переменная лексическая привязка имеет значение 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))))

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

CAVEAT: макрос не будет работать должным образом, если вы попытаетесь определить функцию ** с тем же именем, что и у одной из лексически связанных переменных. Т.е. если вы делаете что-то вроде:

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

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

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

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

Я не уверен в Emacs Lisp, но, насколько я знаю, большое отличие от Common Lisp заключается в том, что он использует динамическую область видимости. Руководство Emacs Lisp утверждает, что Emacs Lisp не имеет замыканий.

Я постараюсь применить свои теоретические знания в области динамического определения объема работ.

Если у вас есть функция id который просто возвращает значение my-id:

(defun id ()
  мой ID)

и вы используете его в какой-то другой функции:

(defun some-other-place ()
  (Я бы))

и где-то на пути к вызову id ты связываешь my-id например, через let:

(определение даже в другом месте ()
  (пусть ((my-id 5))
    (Некоторые-другое место)))

это должно вернуть 5.

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

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

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

;; -*- 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 ...
Другие вопросы по тегам