let и flet в emacs lisp

Я не знаю, можно ли назвать это канонической формулировкой, но для привязки локальной функции в руководстве GNU я советую использовать 'flet':

(defun adder-with-flet (x)
  (flet ( (f (x) (+ x 3)) )
    (f x))
)

Однако случайно я попробовал (немного поиграв в Scheme) следующее выражение, в котором я привязываю лямбда-выражение к переменной с помощью let, и оно также работает, если я передаю функцию mapcar *:

(defun adder-with-let (x)
  (let ( (f (lambda (x) (+ x 3))) )
    (car (mapcar* f (list x)) ))
)

И обе функции работают:

(adder-with-flet 3)   ==> 6
(adder-with-let 3) ==> 6

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


person hatmatrix    schedule 28.07.2009    source источник
comment
Всем, кто пробует это сделать, обратите внимание, что flet может быть недоступен в используемой вами версии emacs, и в этом случае попробуйте заранее (require 'cl), как указано ниже (flet - это вещь CommonLisp).   -  person Robert    schedule 01.05.2013
comment
Какое руководство GNU рекомендует здесь использовать flet?   -  person Stefan    schedule 27.06.2013
comment
Из Emacs 25.1.1 Описание функции: flet Этот макрос устарел с 24.3; используйте либо cl-flet' or cl-letf '   -  person AAAfarmclub    schedule 01.01.2017


Ответы (4)


В отличие от Scheme, Emacs Lisp - это 2-lisp, что означает, что каждый символ имеет две отдельные привязки: привязку значения и привязку функции. При вызове функции (a b c d) первый символ (a) ищется с помощью привязки функции, остальные (b c d) ищутся с помощью привязки значения. Специальная форма let создает новую (локальную) привязку значения, flet создает новую привязку функции.

Обратите внимание, что использование привязки значения или функции для поиска зависит от позиции в вызове функции (a b c d), а не от типа найденного значения. В частности, привязка значения может разрешить функционирование.

В вашем первом примере вы выполняете привязку функции f (через flet), а затем выполняете поиск функции:

(f ...)

Во втором примере вы привязываете значение f к функции (через let), а затем используете поиск значения:

(... f ...)

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

http://en.wikipedia.org/wiki/Common_Lisp#Comparison_with_other_Lisps

person user58804    schedule 29.07.2009
comment
Спасибо за объяснение! Я вижу, это разница между поиском значений и поиском функций. Я был знаком с соглашением о наличии отдельных пространств имен для функций и переменных, но не мог связать это с тем, как функция, связанная как переменная, могла быть вызвана mapcar *. - person hatmatrix; 29.07.2009

Я быстро поискал руководство по Emacs lisp и не смог найти ни одной ссылки на 'flet, что неудивительно, поскольку это часть cl - пакет common-lisp.

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

т.е. это работает:

(let ((myf (lambda (x) (list x x))))
  (eval (list myf 3)))

но

(let ((myf (lambda (x) (list x x))))
  (myf 3))

завершается с ошибкой: "Ошибка Lisp: (void-function myf)"

flet, с другой стороны, выполняет привязку к функциональной ячейке, поэтому это работает:

(flet ((myf (x) (list x x)))
  (myf 3))

Обратите внимание на разницу в том, что flet позволяет вам использовать символ myf напрямую, тогда как let нет - вам нужно использовать некоторую косвенность, чтобы получить функцию из «ячейки значения» и применить ее соответствующим образом.

В вашем примере «mapcar» эквивалентно моему использованию 'eval.

person Trey Jackson    schedule 28.07.2009
comment
Спасибо за ваш ответ! Вместе с объяснением Зиелая я вижу, как работает эта «eval». Да, похоже, что флет находится в расширении cl; Я изначально читал, что (require 'cl) был нужен перед использованием flet, но я думаю, что в новых emacs это уже не так ... - person hatmatrix; 29.07.2009
comment
До того, как расширение cl было отправлено с Emacs из коробки, как люди обрабатывали определения функций, которые нужно было объявить с помощью let-подобной семантики? Было ли принято, что eval - это способ сделать это? - person d11wtq; 17.05.2013

@ d11wq для этого есть `funcall '. Следующие работы:

(defun adder-with-let (x)
  (let ((f #'(lambda (x) (+ x 3))))
    (funcall f 3)))

(adder-with-let 3) ;=> 6
person carltonf    schedule 27.06.2013

Вам не нужно использовать flet, если вы не хотите. Вы помещаете функцию в функциональную ячейку локального символа, определенного с помощью let, как в следующем примере:

(let ((ALocalSymbol))
  (fset 'ALocalSymbol (lambda (x) (* 2 x)))
  (ALocalSymbol 4)
  )

Оценка вернет 8. Обратите внимание на цитату перед ALocalSymbol в (let ((ALocalSymbol))...). В то время как setq цитирует символы, fset - нет.

flet - это своего рода синтаксический сахар. Использование старого простого let для определения нулевых символов позволяет вам выбрать, какую «ячейку» символа установить. Вы можете использовать setq для установки ячейки значения символа или fset для установки ячейки функции.

Надеюсь это поможет,

Пабло

person Pablo A Perez-Fernandez    schedule 17.07.2017
comment
Это совершенно неправильно и связывает (единственную) глобальную функциональную ячейку для интернированного символа ALocalSymbol. (Только ячейки value привязаны к let.) Вы могли бы сделать это, если бы вы создавали новый (неорганизованный) символ, а не просто let-привязку (значение) интернированный символ. - person phils; 18.07.2017
comment
Для ясности: этот код определяет функцию ALocalSymbol в пространстве имен глобальной функции. Если функция с таким именем уже была определена, она уничтожается. - person phils; 18.07.2017
comment
Также обратите внимание, что если вы создали неинтернированный символ, (ALocalSymbol 4) все равно будет вызывать функцию интернированного символа, поэтому вам нужно будет funcall (или аналогичный) ваш неинтернированный символ / функцию. - person phils; 18.07.2017
comment
Я только что понял, что вы правы. В итоге я создал глобальный символ! Спасибо за разъяснения. Это фактически объясняет, почему какой-то код, который я написал, работает некорректно. - person Pablo A Perez-Fernandez; 18.07.2017