Как создать временную функцию в Emacs Lisp - PullRequest
25 голосов
/ 17 апреля 2010

Я делаю несколько утомительных вызовов для множества функций, но параметры будут определены во время выполнения. Я написал простую функцию, чтобы мой код оставался СУХИМ, но указывать его имя не нужно. Я не использую эту функцию где-либо еще.

Я пытаюсь сделать это так, как в Схеме, но я получаю void-function ошибку:

(let ((do-work (lambda (x y z)
                  (do-x x)
                  (do-y y)
                  ;; etc
                  )))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

Я мог бы вставить все это в apply (например, (apply (lambda ...) (cond ...))), но это не очень читабельно. Есть ли лучший способ?

Ответы [ 3 ]

29 голосов
/ 17 апреля 2010

Как и другие списки (но не Scheme), Emacs Lisp имеет отдельные пространства имен для переменных и функций (т.е. это 'Lisp 2 ', а не 'Lisp 1 '; см. Технические вопросы разделения в функциональных ячейках и ячейках значений для определения происхождения и значения этих терминов).

Вам потребуется использовать funcall или apply для вызова лямбда-функции (или другой функции), которая хранится в переменной.

(cond (test-1 (funcall do-work 'a 'b 'c))
      (test-2 (funcall do-work 'i 'j 'k))

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

Внутренний механизм состоит в том, что каждый символ имеет несколько «ячеек» . Какая ячейка используется, зависит от , где символ находится в оценочной форме . Когда символ является первым элементом оценочной формы, используется его ячейка «функции» . В любой другой позиции используется ее ячейка «значения» . В вашем коде do-work имеет функцию в своей ячейке значения. Для доступа к нему в качестве функции вы используете funcall или apply. Если бы он был в ячейке функции, вы могли бы вызвать его напрямую, используя его имя в качестве машины оценочной формы. Вы можете сделать это с помощью flet или labels из пакета cl .

21 голосов
/ 17 апреля 2010

Сделайте (require 'cl), чтобы получить пакет Common Lisp, затем используйте flet вместо let:

(flet ((do-work (x y z)
          (do-x x)
          (do-y y)
          ;; etc
          ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))
4 голосов
/ 17 апреля 2010

Вы можете сделать это способом ANSI Common Lisp (хотя я думаю, что есть некоторые разработки Emacs, которые придадут вам неприятный вид):

(flet ((do-work (x y z)
                (do-x x)
                (do-y y)
                ;; etc
                ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

Не знаю, если вам сначала понадобится (require 'cl) (или cl-macs?), Чтобы использовать flet. Если вы хотите определить рекурсивные функции, вам нужно использовать labels IIRC.

...