Как мне сделать замыкания в Emacs Lisp? - PullRequest
26 голосов
/ 27 февраля 2009

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

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

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

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

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

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

Ответы [ 8 ]

29 голосов
/ 27 февраля 2009

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

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

(funcall (foo 10)) ;; => 10
14 голосов
/ 30 декабря 2012

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

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

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

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

Байтовый компилятор выдает предупреждение, если вы используете 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))))

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

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

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

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

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

8 голосов
/ 27 февраля 2009

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

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

(funcall (foo 10))  ;; => 10
7 голосов
/ 27 февраля 2009

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

6 голосов
/ 27 февраля 2009

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

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

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

(defun id ()
  my-id)

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

(defun some-other-place ()
  (id))

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

(defun even-elsewhere ()
  (let ((my-id 5))
    (some-other-place)))

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

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

4 голосов
/ 04 ноября 2012

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

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

0 голосов
/ 11 мая 2019
;; -*- 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 ...
0 голосов
/ 05 сентября 2011
...