`
zhou2324
  • 浏览: 18441 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

闭包(closure)--Martin Fowler

阅读更多
如有任何疑问,请参阅:http://www.martinfowler.com/bliki/Closure.html

随着人们对动态语言兴趣的增加,更多的人开始关注一个被称之为闭包(closures)或者块区(blocks)的概念,
有c/c++/java/c#(这些语言不支持闭包)背景的程序员并不知道闭包是什么。下面简单的解释了这个概念。

闭包出现迄今为止有一段时间了,我第一次使用闭包是在SmallTalk上,当时被称之为块(block),Lisp使用了闭包,他们同样出现在Ruby脚本语言之中,这也是大部分Ruby爱好者使用Ruby进行脚本编程的原因。

闭包的核心意义是一段可以作为参数被其他函数调用的代码,我将用一个简单的例子来说明,假设我有一批工人的名单,而我想得到其中同时又身为管理人员的那部分工人的名单,对于这部分工人我用一个isManager来标记,使用C#,我们可以这样来编码:
public static IList Managers(IList emps) {
    IList result = new ArrayList();
    foreach(Employee e in emps)
      if (e.IsManager) result.Add(e);
    return result;
  }

而在支持闭包的语言中,我们可以用另一种写法,比如在Ruby中,我们可以这样:
def managers(emps)
  return emps.select {|e| e.isManager}
end
select是定义在Ruby集合类中的一个方法,接受一段代码,一个闭包,作为它的参数,在ruby中你(In ruby you write that block of code between curlies (not the only way))。如果这段代码接受任何你在事先在“|”中间声明的参数,select迭代作为参数的数组,对数组中每一个元素执行那一段代码,将所有返回结果确定为true的元素放入一个数组中,然后返回这个数组。

看完了这个例子,如果你是一名C程序员你可能会觉得你能够使用指针做同样的事情,如果你是一名JAVA程序员你会认为你能够使用匿名内部类实现,如果是C#程序员的话你可能会考虑代理(delegate),这些机制类似于闭包,但是他们有2个不同的地方。

首先是形似上的不同,闭包的内部可以拥有一个本地变量,考虑下面定义的方法:
def highPaid(emps)
  threshold = 150
  return emps.select {|e| e.salary > threshold}
end

你可以看到在select代码块中有一个变量是在本方法中定义的,而在许多不支持闭包的语言中并不能像这样做,利用闭包你可以做更加有趣的事情,看看下面的函数:

def paidMore(amount)
  return Proc.new {|e| e.salary > amount}
end

这个函数返回了一个闭包,在个函数的行为依赖于你传递给它的参数,我可以穿入一个值作为参数来创建一个这样的函数。

highPaid = paidMore(150)

highPaid包含了一段代码(在Ruby中被称为Proc),这段代码根据测试参数对象的工资是否超过150而返回true或者false,我可以这样来使用它:

john = Employee.new
john.salary = 200
print highPaid.call(john)

highPaid.call(john)调用我们前面定义过的代码e.salary>amount,这这里amount是150,这是我们前面创建该Proc时定义好的,当我调用print函数的时候,即使150已经超出了这个范围,但这个绑定依然有效。(Even if that 150 value went out of scope when I issue the print call, the binding still remains.)

所以第一个关键的地方是:闭包是一段代码加上对它们所在环境的绑定,这是使得闭包同C中的函数指针和其它语言中类似机制(比如java中的匿名内部类和C#中的代理)区别开来的一个因素。(java中只有final匿名内部类可以访问本地变量)

如果你不经过大量的实践的话,第二个不同点看起来并不如第一个不同点那么明显,但照样是也是非常重要的原因。支持闭包的语言允许你用很少量的语法来定义它们,这点看起来并不重要,但我认为很关键。这是为什么频繁运用闭包的原因,看看LISP,SMALLTALK,或者RUBY代码,你将会看到到处都在使用闭包语法--比起其他的语言中使用类似语法要频繁得多。能够访问本地变量和绑定运行上下文是一个原因,但我认为更重要的原因是这样使得代码更加简单清晰。

我来举一个很恰当的例子,将一个SmallTalk闭包语法的例子引入JAVA中。起初大部分人,包括我在内,都会使用匿名内部类去取代SMALLTALK中的闭包代码块,但最终的代码往往会变得非常丑陋和混乱,所以我们不得不放弃。

就像软件行业中其他的术语一样,人们并不能给出关于闭包的精确定义。一些人认为闭包仅仅应用于一个包括对自身环境绑定的实际值(the term only applies to an actual value that includes bindings from its environment),就像前述中highPaid函数所返回的那个值,另一些人认为闭包是指有能力绑定所在环境的程序构造(Others use the term 'closure' to refer to a programming construct that has the ability to bind to its environment),This debate, an example of the TypeInstanceHomonym, is usually carried out with the politeness and lack of pedantry that our profession is known for.

我经常在RUBY中用到闭包,但是我并不倾向于创建Procs and pass them around.大多数时候我使用闭包是基于围绕CollectionClosureMethods ,类似于前面所看到的select方法,另一个常用法是围绕方法执行,例如当我们处理一个文件。
File.open(filename) {|f| doSomethingWithFile(f)}

这里有一个open方法打开文件,执行代码块,然后关闭文件,This can be a very handy idiom for things like transactions (remember to commit or rollback) or indeed anything where you have to remember to do something at the end. I use this extensively in my xml transformation routines.

闭包的这种用法比起人们在LISP和函数式语言中所做的,其实是用的很少的,但即使是使用得少,当我使用不支持他们的语言编程的时候,我还是非常思念他们。闭包,当你第一次遇见它时它显得微不足道,但很快你就会喜欢上它。
分享到:
评论

相关推荐

    closure-compiler-npm, 用于管理和记录关闭编译器的包,通过npm使用.zip

    closure-compiler-npm, 用于管理和记录关闭编译器的包,通过npm使用 google-closure-compiler 用闭包编译器检查。编译。优化和压缩 Javascript这个库跟踪发布到 npmjs.org 和相关插件的相关问题。 任何与插件无关的...

    前端开源库-closure-loader

    Closure Loader是一个专为前端开发者设计的开源工具,主要用于处理Google闭包库(Closure Library)的依赖管理。这个加载器是Webpack的一个插件,它允许开发者在Webpack构建流程中无缝地整合Closure Library,从而...

    closure-stylesheets, lints,优化和 i18n izes的CSS transpiler.zip

    closure-stylesheets, lints,优化和 i18n izes的CSS transpiler 关闭样式表闭包样式表是对CSS的扩展,它向标准 CSS conditionals conditionals conditionals conditionals conditionals conditionals conditionals

    Laravel开发-closure-table

    这时,"Closure Table"(闭包表)设计模式就显得尤为重要。本篇文章将详细探讨Laravel中如何实现基于闭包表的层级数据管理。 首先,理解闭包表的概念是关键。在数据库设计中,闭包表是一种用于存储树状或层级结构...

    Swift3.0 闭包整理 - CocoaChina_让移动开发更简单1

    Swift3.0 闭包是该语言中的一个重要概念,它是一种可以捕获和存储周围环境中的数据(称为闭包环境或上下文)的匿名函数。闭包在Swift中被广泛用于处理异步操作、排序、过滤等任务。下面将详细阐述Swift3.0中的闭包...

    离散数学-闭包运算-.net

    闭包运算则是离散数学中的一个核心概念,它广泛应用于图论、逻辑学和计算机程序设计中。在本实验中,我们将探讨如何在.NET环境中,利用C#语言实现闭包运算,特别是通过Warshall算法。 闭包运算定义了一种在集合上...

    google-closure-library-v20180405-50-gda9add3.zip

    这个压缩包"google-closure-library-v20180405-50-gda9add3.zip"包含了2018年4月5日版本的Closure Library,版本号为50-gda9add3。这个库是开源的,被广泛应用于各种项目,其设计目标是提供高效、可维护的代码,同时...

    数据库-属性闭包代码-python

    数据库老师要求用代码实现求属性闭包,该代码为python代码,注释详细

    Python闭包实例closure.py

    Python闭包实例closure.py 简单示例闭包的使用 简单示例闭包的使用

    closure-compiler-npm:用于管理和记录闭包编译器的软件包,可通过npm使用

    主包装google-closure-compiler软件包包含Grunt和Gulp插件以及一个CLI: 其他套餐裸露的发行版面向希望在特定平台上进行创作的开发人员。 Java版本: 本机Linux版本: 本机OSX构建: 本机Windows版本: 执照版权所有...

    java版ss源码-grunt-closure-tools:用于grunt的Google关闭工具

    grunt-closure-tools npm install grunt-closure-tools --save-dev 然后通过grunt.js添加到grunt.js来注册任务: grunt . loadNpmTasks ( 'grunt-closure-tools' ) ; Grunt 0.3.x 兼容性 获得咕噜声 0.3.x。 兼容...

    编译原理ε-closure(I)的程序实现(Java实现)课程设计报告.docx

    《编译原理ε-closure(I)的程序实现》课程设计报告主要涵盖了如何利用Java编程语言实现有限自动机(NFA)的空闭包ε-closure(I)计算。该设计的目标是处理用户输入的任意有限自动机,针对任意状态子集I,输出其空...

    closure闭包

    在正规文法中,E-closure是对一个正规集进行扩展,包含所有可以通过ε(空串)转换到达的符号。这个过程对于构建正规表达式的有限状态自动机(如下推自动机PDA)非常重要。 在实践中,我们可以通过以下步骤求取一个...

    PHP7 新特性:常量数组、匿名类、Closure-call().md

    - **实现方式**:通过 `Closure::call()` 方法指定闭包调用时的 `$this` 对象实例和参数。 - **示例代码**: ```php class MyClass { public $value = 'Default'; public function getClosure() { return ...

    superstartup-closure-compiler:超级初创公司风味的 Google Closure Compiler

    npm install superstartup-closure-compiler --save-deps --silent 几乎没有atm,只有两种方法: 获取路径() 获取闭包编译器.jar文件的相对路径。 获取路径SS() 获取超级启动编译器.jar文件的相对路径。 例子: var...

    closure-compiler:围绕 Google Closure Compiler 的 PHP Wrapper

    闭包编译器 什么是闭包编译器? 来自: Closure Compiler 是一个让 JavaScript 下载和运行速度更快的工具。 它是一个真正的 JavaScript 编译器。 它不是从源语言编译为机器代码,而是从 JavaScript 编译为更好的 ...

    closure-compiler,javascript检查器和优化器。.zip

    闭包编译器是一个让javascript下载和运行更快的工具。它是一个真正的javascript编译器。它不是从源语言编译成机器代码,而是从javascript编译成更好的javascript。它解析你的javascript,分析它,删除死代码,重写并...

    closure-calculate-chunks

    node --preserve-symlinks node_modules/closure-calculate-chunks/index.js --entrypoint ./src/js/entry.js 注意:应使用--preserve-symlinks选项启动使用该库的节点进程,否则返回的文件路径可能与节点模块解析...

Global site tag (gtag.js) - Google Analytics