Jamie Zawinski在他的(1997)article "java sucks"中使用该术语,就好像你应该知道它意味着什么:
我真的很讨厌缺乏向下的乐趣;匿名类是一个蹩脚的替代品。 (我可以在没有长寿命的闭包的情况下生活,但我发现缺少函数指针是一个巨大的痛苦。)
这似乎是Lisper的俚语,我可以找到以下简短的定义here,但不知何故,我想我仍然没有得到它:
许多闭包仅在它们引用的绑定范围内使用;这些在Lisp用语中被称为“向下的funargs”。
如果不是Steve Yegge,我现在感觉很愚蠢,但似乎可以问:
杰米·扎温斯基是个英雄。一个活生生的传奇[...] 一个可以使用“向下的funargs”这个词的人,然后瞪着你,只是敢于让他去解释它,你克里汀。
那么有一个Lisper可以为像我这样的C风格的程序员编译吗?
答案 0 :(得分:52)
向下funargs是本地函数,不返回或以其他方式保留其声明范围。它们只能从向下传递到当前范围内的其他函数。
两个例子。这是一个向下的乐趣:
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;
}
答案 1 :(得分:28)
为了更好地了解该术语的来源,您需要了解一些历史记录。
旧Lisp黑客之所以可以将向下 funargs与funargs区别开来,原因在于,在缺乏词汇变量的传统Lisp中,向下的funargs很容易实现,而一般情况很难。 / p>
传统上,通过在环境中添加绑定(变量的符号名称,与其值配对),在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 ...)
你可以看到,在这样的实现中,向下 funargs真的很容易实现,因为在创建函数时环境中的变量仍然会在函数的环境中它被评估了。
这样的变量称为特殊或动态变量,您可以使用special
声明在Common Lisp中创建它们。
答案 2 :(得分:13)
在Common Lisp中:
(let ((a 3))
(mapcar (lambda (b) (+ a b))
(list 1 2 3 4)))
-> (4 5 6 7)
在上面的形式中,lambda函数向下传递。当由高阶函数MAPCAR(它获取函数和值列表作为参数,然后将函数应用于列表的每个元素并返回结果列表)调用时,lambda函数仍然引用变量来自LET表达的'a'。但它发生在LET表达式中。
将以上版本与此版本进行比较:
(mapcar (let ((a 3))
(lambda (b) (+ a b)))
(list 1 2 3 4))
这里从LET返回lambda函数。向上一点。然后它被传递给MAPCAR。当MAPCAR调用lambda函数时,其周围的LET不再执行 - 仍然该函数需要从LET引用变量'a'。
答案 3 :(得分:12)
Wiki上有一篇很有描述性的文章,名为Funarg problem
“向下的funarg也可以参考 该函数的函数状态 实际上并没有执行。然而, 因为,根据定义,存在 一个向下的funarg包含在 执行该功能 创建它,激活记录 功能通常仍然可以 存储在堆栈中。“