据我所知,结合其他人的知识,在主流语言中
- 目标 C
- C#
- VB.net
爪哇- Python
- 红宝石
- Javascript
- 语言
- Perl
有闭包和匿名函数。普通的 C/C++ 没有这些。
这些语言中的闭包是否具有相同的语义?它们对于日常编程有多重要?
一些背景知识:我一直在阅读针对 Apple 的 Grand Central Dispatch 的Objective C的新增功能,并认为我应该了解是否真的只有一种或不同的方法可以将块状结构引入语言。
据我所知,结合其他人的知识,在主流语言中
有闭包和匿名函数。普通的 C/C++ 没有这些。
这些语言中的闭包是否具有相同的语义?它们对于日常编程有多重要?
一些背景知识:我一直在阅读针对 Apple 的 Grand Central Dispatch 的Objective C的新增功能,并认为我应该了解是否真的只有一种或不同的方法可以将块状结构引入语言。
请澄清您的问题:您对工作是什么意思?您如何使用它们,何时使用它们或如何在内部实现它们?
.NET 语言 (VB/C#) 的编译过程可以向您展示通常如何表示闭包:
闭包被翻译成一个匿名类,其中包含用于封闭变量的字段。访问闭包的函数指针(委托)只不过是匿名类实现的特殊方法的指针。
关于闭包重要性的一些说明:
.NET:闭包的显式用法并不多。匿名函数有时用于事件和数据的功能表示。主要用途是 LINQ 查询的(内部)表示。
Python:对匿名函数的支持很差 - 仅限lambda vars: expr
- 语法。
Javascript:整个函数语法不过是围绕匿名函数和闭包的语法糖!
function f(x) { return x + 1; }
等于
var f = function(x) { return x + 1; }
Ruby:大量使用闭包:大多数程序流结构都依赖于闭包。看
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 除外-您有匿名类,而不是函数!)都将允许这样的事情。
主流语言之间的主要语义差异是是否允许更改闭包捕获的变量。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
is 不仅start
在所有 5 个闭包中共享,start++
而且在 for 循环结束时,repeat 给它的值是 5。在混合范式语言中,我的观点是 Java 和 Python 有正确的想法——如果你想改变捕获的变量,你最好被迫创建一个类,这使得捕获过程明确,当你通过例如,将它们传递给构造函数。我喜欢为函数式编程保留闭包。
顺便说一下,Perl 也有闭包。
Java 在他们的设计中基本上排除了闭包(尽管现在听起来可能很矛盾),因为他们希望在许多方面保持语言的简单性。
然而,从第 1 天v1.1 开始,Java 就支持相同的功能,但不是混合范式(函数式与 OO ),而是允许存在几乎用于相同目的的匿名内部类。由于它们不是闭包而是类,因此您必须输入更多字符。
因此,例如,有一个 Timer 接收 TimerTask 作为要执行的块,您将编写:
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 的一个有趣的演讲:“闭包争议”
这是一篇关于“进化 Java 语言的原则”的文章
Java 没有闭包或匿名函数,尽管它确实有匿名类可以用来在某种程度上模拟这些。
在 Java 的下一个版本中,有一些相互竞争的提议将闭包添加到该语言中,但这些都没有包含在官方更改列表中。