Что такое "нисходящие фунгары"? - PullRequest
56 голосов
/ 24 февраля 2009

Джейми Завински использует этот термин в своей (1997) статье "java sucks" , как будто вы должны знать, что это значит:

Я действительно ненавижу отсутствие нисходящих funargs; анонимные классы являются слабым заменителем. (Я могу жить без долгоживущих замыканий, но я нахожу отсутствие указателей на функции огромной болью.)

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

Многие замыкания используются только во время привязки, к которой они относятся; на языке Lisp они известны как "нисходящие фунгары".

Если бы не Стив Йегге , я бы сейчас чувствовал себя глупо, но, кажется, можно спросить:

Джейми Завински - герой. Живая легенда. [...] Парень, который может использовать термин "нисходящий фунгарг", а затем смотрит на тебя, просто осмеливаясь попросить его объяснить, кретин.

- XEmacs мертв, да здравствует XEmacs

Так есть ли здесь Лиспер, который может скомпилировать это для программистов в стиле C, таких как я?

Ответы [ 4 ]

52 голосов
/ 24 февраля 2009

Нисходящие фунгары - это локальные функции, которые не возвращаются или иным образом выходят из области своего объявления. Они могут быть переданы только вниз другим функциям из текущей области.

Два примера. Это нисходящий funarg:

function () {
    var a = 42;
    var f = function () { return a + 1; }
    foo(f); // `foo` is a function declared somewhere else.
}

Пока это не так:

function () {
    var a = 42;
    var f = function () { return a + 1; }
    return f;
}
28 голосов
/ 25 февраля 2009

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

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

Традиционно локальная переменная была реализована в интерпретаторе Lisp путем добавления привязки (имя символа переменной в паре со ее значением) в среду . Такое окружение было просто реализовать с помощью списка ассоциаций. Каждая функция имела свое собственное окружение и указатель на окружение родительской функции. Ссылка на переменную была разрешена путем просмотра в текущей среде, а если ее там нет, то в родительской среде и т. Д. В стеке сред, пока не будет достигнута глобальная среда.

В такой реализации локальные переменные shadow глобальные переменные с одинаковыми именами. Например, в Emacs Lisp print-length - это глобальная переменная, которая задает максимальную длину списка для печати до его сокращения. Связав эту переменную вокруг вызова функции, вы можете изменить поведение операторов print в этой функции:

(defun foo () (print '(1 2 3 4 5 6))) ; output depends on the value of print-length

(foo) ; use global value of print-length
  ==>  (1 2 3 4 5 6)

(let ((print-length 3)) (foo)) ; bind print-length locally around the call to foo.
  ==>  (1 2 3 ...)

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

Переменные, которые действуют так, называются специальными или динамическими переменными, и вы можете создать их в Common Lisp, используя объявление special.

13 голосов
/ 24 февраля 2009

В Common Lisp:

(let ((a 3))
  (mapcar (lambda (b) (+ a b))
          (list 1 2 3 4)))

->  (4 5 6 7)

В вышеприведенной форме лямбда-функция передается ВНИЗ. При вызове функцией MAPCAR высшего порядка (которая получает функцию и список значений в качестве аргументов, а затем применяет функцию к каждому элементу списка и возвращает список результатов), лямбда-функция по-прежнему ссылается на переменную 'a' из выражения LET. Но все это происходит в выражении LET.

Сравните выше с этой версией:

(mapcar (let ((a 3))
          (lambda (b) (+ a b)))
        (list 1 2 3 4))

Здесь лямбда-функция возвращается из LET. Немного вверх. Затем он передается в MAPCAR. Когда MAPCAR вызывает лямбда-функцию, ее окружение LET больше не выполняется - все же функция должна ссылаться на переменную 'a' из LET.

12 голосов
/ 24 февраля 2009

В Вики есть довольно описательная статья под названием Проблема Funarg

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

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