不同的编程语言如何使用闭包?

时间:2009-09-14 17:56:33

标签: functional-programming lambda closures anonymous-function grand-central-dispatch

据我所知,结合主流语言中的他人知识

  • 目标C
  • C#
  • VB.net
  • 爪哇
  • 的Python
  • 红宝石
  • 的Javascript
  • Lisp的
  • 的Perl

有闭包和匿名函数。普通的C / C ++没有这些。

这些语言中的闭包具有相同的语义吗?它们对日常编程有多重要?

一些背景:我一直在阅读针对Apple的Grand Central Dispatch的 Objective C的新增内容,并认为我应该知道是否真的只有一种不同的方法可以将类似块的结构引入语言中。

4 个答案:

答案 0 :(得分:5)

请澄清您的问题:您对工作的意思是什么?您如何使用它们,何时使用它们或如何在内部实施它们?

编译.NET语言(VB / C#)的过程可以向您展示如何通常表示闭包:

闭包被转换为一个匿名类,其中包含封闭变量的字段。访问闭包的函数指针(委托)只不过是匿名类实现的特殊方法的指针。

关于闭包重要性的一些注释:

  • .NET: 没有明确使用闭包。匿名函数有时用于事件和数据的功能表示。主要用途是LINQ查询的(内部)表示。

  • 的Python: 对匿名函数的支持不足 - 仅lambda vars: expr - 语法。

  • Javascript:整个函数语法只不过是匿名函数和闭包的语法糖!

    function f(x) { return x + 1; }
    

    等于

    var f = function(x) { return x + 1; }
    
  • 红宝石: 大量使用闭包:大多数程序流结构都依赖于它们。 参见

    array.each do |x|
       # code
    end
    

    只是对array#each函数的调用,其中一个匿名函数作为参数传递(块)。用C#表示:

    Array.Each(x => {
        // Code
    })
    

通常,闭包是这样的:

# Enclose `i` - Return function pointer
def counter():
    i = 0
    def incr():
        i += 1
        print(i)
    return incr

c = counter()
c() # -> 1
c() # -> 2
c() # -> 3

您枚举的所有语言(Java除外 - 您有匿名类,而不是函数!)将允许这样的事情。

答案 1 :(得分:3)

Java基本上排除了设计中的闭包(虽然现在听起来可能相互矛盾),因为他们希望在许多方面保持语言简单。

第1天 v1.1以来,Java已经支持相同的功能,但是它们不是混合范式(功能与OO),而是允许存在匿名内部类,它们几乎可以用于同样的目的。因为它们不是闭包,而是类,你必须输入更多的字符。

因此,例如,要有一个接收TimerTask的Timer作为要执行的块,你可以写:

 Timer timer = new Timer();

 timer.schedule( new TimerTask() {  // this is like the code block. 
     public void run() {
          System.out.println("Hey!");
     }
 },0);

如您所见,“TimerTask(){...”是匿名内部类的定义。该实例也可以分配给作为参数传递的变量。

TimerTask task = new TimerTask() {
     public void run() {
     }
 };


....
timer.schedule( task , 0 ) ;

你可以拥有这种结构,但它们不是闭包。

闭包仍然存在争议,需要添加到Java编程语言中。

以下是Joshua Bloch的有趣演讲:"The closures controversy"

以下是关于"Principles for Evolving the Java Language"

的文章

答案 2 :(得分:2)

主流语言之间语义的主要故意差异在于是否允许更改由闭包捕获的变量。 Java和Python说不,其他语言都说是(好吧,我不知道Objective C,但其余的都是)。能够更改变量的优点是您可以编写如下代码:

public static Func<int,int> adderGen(int start) {
    return (delegate (int i) {      // <-- start is captured by the closure
                start += i;         // <-- and modified each time it's called
                return start;
            });
}
// later ...
var counter = adderGen(0);
Console.WriteLine(counter(1)); // <-- prints 1
Console.WriteLine(counter(1)); // <-- prints 2
// :
// :

你会发现这比同等的计数器类少得多,尽管C#(这里使用的语言)在幕后为你生成了完全相同的代码。 缺点是捕获的变量确实是共享的,所以如果你在经典for循环中生成一堆加法器,你就会感到惊讶......

var adders = new List<Func<int,int>>();

for(int start = 0; start < 5; start++) {
    adders.Add(delegate (int i) {
        start += i;
        return start;
    });
}

Console.WriteLine(adders[0](1)); // <-- prints 6, not 1
Console.WriteLine(adders[4](1)); // <-- prints 7, not 5

不仅start在所有5个闭包中共享,重复的start++在for循环结束时给它赋值5。在一种混合范式语言中,我的意见是Java和Python有正确的想法 - 如果你想改变捕获的变量,你最好被迫改造一个类,这使得例如,当您将它们传递给构造函数时,捕获进程显式。我喜欢为函数式编程保留闭包。

顺便说一句,Perl也有关闭。

答案 3 :(得分:0)

Java没有闭包或匿名函数,尽管它有一些匿名类可以用来在某种程度上模拟它们。

在下一版Java中,有一些竞争性的建议要求在语言中添加闭包,但这些都没有包含在官方变更列表中。