什么是“向下的funargs”?

时间:2009-02-24 10:09:08

标签: functional-programming lisp terminology

Jamie Zawinski在他的(1997)article "java sucks"中使用该术语,就好像你应该知道它意味着什么:

  

我真的很讨厌缺乏向下的乐趣;匿名类是一个蹩脚的替代品。 (我可以在没有长寿命的闭包的情况下生活,但我发现缺少函数指针是一个巨大的痛苦。)

这似乎是Lisper的俚语,我可以找到以下简短的定义here,但不知何故,我想我仍然没有得到它:

  

许多闭包仅在它们引用的绑定范围内使用;这些在Lisp用语中被称为“向下的funargs”。

如果不是Steve Yegge,我现在感觉很愚蠢,但似乎可以问:

  杰米·扎温斯基是个英雄。一个活生生的传奇[...] 一个可以使用“向下的funargs”这个词的人,然后瞪着你,只是敢于让他去解释它,你克里汀。

     

- XEmacs is dead, long live XEmacs

那么有一个Lisper可以为像我这样的C风格的程序员编译吗?

4 个答案:

答案 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包含在   执行该功能   创建它,激活记录   功能通常仍然可以   存储在堆栈中。“