封闭的确切定义是什么?
我已经阅读了关于stackflow和其他来源的封闭的话题,还有一件事仍然令我困惑。 从技术上来说,我已经能够将闭包简单地归结为包含函数代码和该函数中绑定variables值的数据集合。
换句话说,从我的理解来说,从技术上讲,下面的C函数应该是一个闭包:
int count() { static int x = 0; return x++; }
然而,我所读的一切似乎意味着闭包必须以某种方式将函数作为第一类对象传递。 另外,通常似乎暗示封锁不是程序性编程的一部分。 这是一个解决scheme与解决的问题过度相关的情况,还是我误解了确切的定义?
从我所了解的闭包也必须能够访问调用上下文中的variables。 闭包通常与function编程相关联。 语言可以有不同types的编程观点,function,程序,命令,声明等元素。他们的名字从一个特定的上下文closures。 它们也可以具有词汇绑定,因为它们可以引用具有在该上下文中使用的相同名称的指定上下文。 你的例子没有引用任何其他的上下文,但全球静态。
从维基百科
闭包closures了自由variables(variables不是局部variables)
不,这不是封闭的。 你的例子只是一个函数,返回增加一个静态variables的结果。
以下是closures如何工作:
function makeCounter( int x ) { return int counter() { return x++; } } c = makeCounter( 3 ); printf( "%d" c() ); => 4 printf( "%d" c() ); => 5 d = makeCounter( 0 ); printf( "%d" d() ); => 1 printf( "%d" c() ); => 6
换句话说,不同的调用makeCounter()产生不同的函数,在它们的词汇环境中它们自己绑定了variables,而这些variables已经被“封闭”了。
编辑:我认为像这样的例子使闭包比定义更容易理解,但是如果你想要一个定义,我会说:“闭包是一个函数和一个环境的组合,环境包含在函数中定义的variables以及在创build函数时可见的函数,只要函数存在,这些variables必须保持可用。
对于确切的定义,我build议看看它的维基百科条目 。 特别好。 我只是想用一个例子来澄清一下。
假设这个C#代码片段(应该在列表中执行AND
search):
List<string> list = new List<string> { "hello world", "goodbye world" }; IEnumerable<string> filteredList = list; var keywords = new [] { "hello", "world" }; foreach (var keyword in keywords) filteredList = filteredList.Where(item => item.Contains(keyword)); foreach (var s in filteredList) // closure is called here Console.WriteLine(s);
在C#中,这是一个常见的陷阱。 如果您查看Where
中的lambdaexpression式,您会看到它定义了一个函数,它的行为取决于定义站点上variables的值。 就像将一个variables本身传递给该函数,而不是该variables的值 。 实际上,当这个闭包被调用时,它会在那个时候检索keyword
variables的值。 这个例子的结果是非常有趣的。 它打印出“你好世界”和“再见世界”,这不是我们想要的。 发生了什么? 正如我上面所说的,我们用lambdaexpression式声明的函数是keyword
variables的closures,所以这是发生了什么事情:
filteredList = filteredList.Where(item => item.Contains(keyword)) .Where(item => item.Contains(keyword));
而在closures执行时, keyword
的值是“世界”,所以我们基本上用相同的关键字过滤列表几次。 解决scheme是:
foreach (var keyword in keywords) { var temporaryVariable = keyword; filteredList = filteredList.Where(item => item.Contains(temporaryVariable)); }
由于temporaryVariable
的范围是foreach
循环的主体 ,所以在每次迭代中它都是一个不同的variables。 实际上,每个闭包都会绑定到一个独特的variables(这些variables是每次迭代的temporaryVariable
variables的不同实例)。 这一次,它会给出正确的结果(“hello world”):
filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1)) .Where(item => item.Contains(temporaryVariable_2));
其中temporaryVariable_1
的值为“hello”, temporaryVariable_2
的值为“world”。
请注意,闭包导致了variables生命周期的延长(它们的生命应该在循环的每次迭代之后结束)。 这也是封闭的一个重要副作用。
闭包是用本地状态表示过程/函数的实现技术。 SICP描述了一种实现封闭的方法。 无论如何,我将介绍它的要点。
所有expression式,包括函数在环境中被评估。环境是一系列的框架 。 一个框架将variables名称映射到值。 每个框架还有一个指向它的封闭环境的指针。 一个函数在一个新的环境中进行评估,一个框架包含它的参数的绑定。 现在让我们看看以下有趣的场景。 想象一下,我们有一个称为累加器的函数,当计算时,将返回另一个函数:
// This is some C like language that has first class functions and closures. function accumulator(counter) { return (function() { return ++counter; }); }
当我们评估以下行时会发生什么?
accum1 = accumulator(0);
首先创build一个新的环境,一个整数对象(对于计数器 )在第一帧被绑定为0。 返回的值是一个新的函数,绑定在全局环境中。 一旦function评估结束,通常新的环境将被垃圾收集。 这里不会发生。 accum1持有对它的引用,因为它需要访问variables计数器 。 当调用accum1时 ,它将在参考环境中递增计数器的值。 现在我们可以使用本地状态或闭包调用accum1函数。
我在我的博客http://vijaymathew.wordpress.com上描述了closures的一些实际用途。; (请参阅文章“危险devise”和“在消息传递”)。
已经有很多答案了,但我会再添加一个人…
闭包不是function语言所独有的。 他们发生在帕斯卡(和家庭),例如,它有嵌套的程序。 标准C没有(还),但是IIRC有一个GCC扩展。
基本的问题是一个嵌套的过程可能会引用在其父项中定义的variables。 此外,父进程可能会返回对调用者的嵌套过程的引用。
嵌套过程仍然引用父对象本地的variables – 特别是当执行函数引用的行时,这些variables具有的值 – 即使这些variables不再作为父对象退出。
如果该过程永远不会从父级返回,甚至会发生问题 – 在不同时间构造的嵌套过程的不同引用可能使用相同variables的不同过去值。
对此的解决方法是,当引用嵌套函数时,将其封装在包含它稍后需要的variables值的“闭包”中。
Python的lambda是一个简单的function风格的例子…
def parent () : a = "hello" return (lamda : a) funcref = parent () print funcref ()
我的python有点生疏,但我认为是对的。 重点是嵌套的函数(lambda)仍然引用局部variablesa
的值,即使parent
进程被调用时退出。 该函数需要在某个地方保存该值,直到需要时为止,这个地方称为闭包。
闭包有点像一组隐含的参数。
好问题! 鉴于面向对象的OOP原理之一是对象具有行为和数据,因此闭包是一种特殊types的对象,因为它们最重要的目的就是它们的行为。 这就是说,当我谈到他们的“行为”时,我的意思是什么?
(其中很多是由Dierk Konig编写的“Groovy in Action”,这是一本很棒的书)
在最简单的层面上,closures实际上只是一些代码,被封装成一个雌雄同体的对象/方法。 这是一个方法,因为它可以带参数并返回一个值,但是它也是一个对象,因为你可以传递一个对象的引用。
用狄克的话来说,想象一个里面有一张纸的信封。 一个典型的对象会在本文中写入variables和它们的值,但是闭包会有一个指令列表。 假设这封信说“给你的信和信给你的朋友”。
In Groovy: Closure envelope = { person -> new Letter(person).send() } addressBookOfFriends.each (envelope)
这里的闭包对象是包络variables的值,它的用处是它是每个方法的参数。
一些细节:范围:闭包的范围是可以在其中访问的数据和成员。 从闭包返回:闭包通常使用callback机制来执行并从其自身返回。 参数:如果闭包只需要一个参数,Groovy和其他lang提供一个默认名称:“it”,以使编码更快。 例如在我们之前的例子中:
addressBookOfFriends.each (envelope) is the same as: addressBookOfFriends.each { new Letter(it).send() }
希望这是你正在寻找的!
一个对象是状态加function。 封闭,是function加状态。
函数f是closures(捕获)x时的闭包
我认为彼得·埃迪(Peter Eddy)是对的,但这个例子可能会变得更有趣。 你可以定义两个函数closures本地variables,增量和减量。 计数器将在这对函数之间共享,并且对它们是唯一的。 如果你定义了一对新的递增/递减函数,他们将共享一个不同的计数器。
另外,你不需要传入x的初始值,你可以让它在function块内默认为零。 这会使得它更清楚地使用一个你不再有正常访问权限的值。