- 浏览: 143748 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (110)
- CoreJava (18)
- 待解决 (1)
- 数据结构 (3)
- 科普 (2)
- 网站 (1)
- DataBase (8)
- Access (1)
- Asp (1)
- JSP (1)
- 操作系统 (8)
- Tech (1)
- Linux (11)
- Career (5)
- MongoDB (1)
- Embedded (1)
- JavaScript (1)
- UltraIso (1)
- Linux命令 (1)
- DesignPattern (1)
- Ruby (13)
- Vim (1)
- 自考 (2)
- Github (5)
- zrProject (1)
- Emacs (4)
- Math (3)
- Ajax (1)
- 没看懂 (1)
- HTML (1)
- Philosophy (1)
- 软件 (1)
- 面试 (1)
- 考试报名 (3)
- Regex (1)
- 日语 (1)
- 生活 (1)
最新评论
Ruby Closure
- 博客分类:
- Ruby
Closure - programming language construct, an abstraction binding a function to its scope (Wikipedia)
blocks, Procs, Methods, lambdas(也称闭包)是Ruby中最强大的一部分,用过你就会知道,同时也是最容易迷惑的。
这可能是因为Ruby处理闭包的方式有点怪。更甚的是,Ruby有4种处理闭包的方式, 第一次用,每种都不太顺手。
首先:blocks代码块
最常见、最简单、最富争议、最有Ruby风格的方式是blocks。写法如下:
array = [1, 2, 3, 4] array.collect! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16]
do…end构成一个block。然后把这个block通过collect!传给一个数组。就可以使用block中的n来迭代数组中每个元素。
collect!是Ruby库里的方法,下面我们来写一个自己的类似方法iterate!
class Array def iterate! self.each_with_index do |n, i| self[i] = yield(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16]
首先,我们打开Array,并添加进iterate!方法。方法名以!结尾表示危险方法,引起注意。现在我们就可能像使用collect!一样使用iterate!
与属性不同,在方法中不需要指定block的名字,而是使用yield来调用。yield会执行block中的代码。同时,注意我们是怎么把n(each_with_index当前处理的数字)传给yield的。传给yield的参数即对应了block中的参数(||中的部分)。现在n就能被block调用并在yield调用中返回n**2。
整个调用如下:
1、一个整数组成的数组调用iterate!
2、当yield被调用时,把n(第一次为1,第二次为2,…)传给block
3、block对n进行n**2。因为是最后一行,自动作为结果返回。
4、yield得到block的结果,并把值重写到array里。
5、数据中每个对象执行相同操作。
以上仅仅是个开始,yield只是调用block的一种方式,还有一种叫Proc,看看。
class Array def iterate!(&code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16]
和上一段代码只有两个不同
1、为iterate!传递一个参数&code,&表示这个参数是block。
2、在iterate!中没有使用yield而是call。
结果相同,为什么还要这种不同的语法呢?让我们先来看一个到底什么是blocks吧?
def what_am_i(&block) block.class end puts what_am_i {} # => Proc
block竟然是Proc!那Proc是什么?
Procs 过程
blocks很简单,但当我们需要处理很多blocks,多次使用一个blocks时,我们不得不重复代码。既然Ruby是完全面向对象的,我们就能把这些可复用的代码保存成object。这段可复用的代码就是Proc(procedure的简称)
block与Proc惟一的不同是:block是不能保存的Proc,一性的。
class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array_1 = [1, 2, 3, 4] array_2 = [2, 3, 4, 5] square = Proc.new do |n| n ** 2 end array_1.iterate!(square) array_2.iterate!(square) puts array_1.inspect puts array_2.inspect # => [1, 4, 9, 16] # => [4, 9, 16, 25]
注意:并没有在 iterate!的参数头部添加&,因为Proc只是一个普通类,不需要特殊处理。
上面的方式也是大多数语言处理闭包的方式。
而block是Ruby特有的方式。
另外Ruby不只使用blocks做闭包还有一个原因。比如有时我们需要传递多个闭包给一个方法,这时block马上力不从心了。但我们可以用Proc:
def callbacks(procs) procs[:starting].call puts "Still going" procs[:finishing].call end callbacks(:starting => Proc.new { puts "Starting" }, :finishing => Proc.new { puts "Finishing" }) # => Starting # => Still going # => Finishing
所以,什么时候用blocks而不用Procs呢?我一般这样判断:
1、Block:方法把一个对象拆分成很多片段,并且你希望你的用户可以与这些片段做一些交互。
2、Block:希望自动运行多个语句,如数据库迁移(database migration)。
3、Proc:希望多次复用一段代码。
4、Proc:方法有一个或多个回调方法(callbacks)。
为什么block小写,而Proc大写
这只是我个人习惯。因为Proc是Ruby中的一个类似,而blocks并没有自己的类(本质上只是Procs),只是一种语法规则。后面的lambda 小写也是如此。
Lambda 拉姆达表达式
上面的Procs与blocks用法很像其它语言中的匿名函数(即lambdas)。Ruby也支持lambdas.
class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate!(lambda { |n| n ** 2 }) puts array.inspect # => [1, 4, 9, 16]
lambdas看起来很像Procs,但它们有2个细微的区别。
1、lambdas检查参数的个数,Procs不会。
def args(code) one, two = 1, 2 code.call(one, two) end args(Proc.new{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"}) args(lambda{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"}) # => Give me a 1 and a 2 and a NilClass # *.rb:8: ArgumentError: wrong number of arguments (2 for 3) (ArgumentError)
可以看到,在Proc中,多余的参数被设为nil。但lambdas中,Ruby抛出了一个错误。
2、return不同。lambdas的return是返回值给方法,方法会继续执行。Proc的return会终止方法并返回得到的值。有点拗口,下面看例子。
def proc_return Proc.new { return "Proc.new"}.call return "proc_return method finished" end def lambda_return lambda { return "lambda" }.call return "lambda_return method finished" end puts proc_return puts lambda_return
proc_return中,执行到Proc.new中的return时,直接返回”Proc.new”,不继续执行。
lambda_return中,执行到lambda中的return时,返回”lambda”,方法继续执行。
为什么会有这样的不同?
答案在于procedures和methods概念上的不同。
Ruby中的Procs是代码片段(code snippets),不是方法。因此,Proc的return就是整个方法的return。
但lambdas就像是单独的methods(只不过是匿名的),所以它要检查参数个数,且不会覆盖整个方法的返回。
因此,最好把lambdas当作另一种methods的写法,一种匿名的方式。
所以,什么时候用lambda而不是Proc呢?可以参考下面代码:
def generic_return(code) code.call return "generic_return method finished" end puts generic_return(Proc.new { return "Proc.new" }) puts generic_return(lambda { return "lambda" }) # => *.rb:6: unexpected return (LocalJumpError) # => generic_return method finished
Ruby语法中一般参数(例子中为Proc)不能含有return。但使用了lambda后可以用return。
还可以参考:
def generic_return(code) one, two = 1, 2 three, four = code.call(one, two) return "Give me a #{three} and a #{four}" end puts generic_return(lambda { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| x + 2; y + 2 }) puts generic_return(Proc.new { |x, y| [x + 2, y + 2] }) # => Give me a 3 and a 4 # => *.rb:9: unexpected return (LocalJumpError) # => Give me a 4 and a # => Give me a 3 and a 4
使用lambda,代码很自然。但如果用Proc,我们需要对Arrays进行赋值。
Method 对象
当你想把一个方法以闭包的形式传递给另一个方法,并且保持代码DRY。你可以使用Ruby的method方法。
class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end def square(n) n ** 2 end array = [1, 2, 3, 4] array.iterate!(method(:square)) puts array.inspect # => [1, 4, 9, 16]
例子中,我们先有了一个square方法。我们可以把它转换成一个Method对象并以参数形式传递给iterate!方法。但,这个新对象属于哪个类呢?
def square(n) n ** 2 end puts method(:square).class # => Method
如你所料,square不是Proc,而是Method。Method与lambda用法相同,因为它们的概念是一样的。不同的是Method是有名字的method,而lambda是匿名method.
总结
到此为止,我们已经了解了Ruby的4种闭包类型:blocks, Procs, lambdas 和 Methods。
blocks和Procs看起来像在代码中插入代码片段。而lambdas和Methods看起来像方法。
通过几个例子和比较,希望你能了解如何灵活运用闭包,游刃有余!
译至:http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
发表评论
-
使用Net::SSH和Net::SCP编写Linux服务器管理脚本
2012-06-14 19:20 2364原文:http://rubyer.me/blog/113 ... -
Ruby的 private protect public
2012-06-14 19:18 1310原文:http://rubyer.me/bl ... -
ruby-mp3info,纯Ruby修改mp3标签
2012-06-14 19:16 868原文:http://rubyer.me/blog/747 ... -
Ruby/Rails 生態圈 Ecosystem
2012-06-13 21:59 801http://ihower.tw/rails3/eco ... -
提高rails new时bundle install运行速度
2012-05-05 20:24 0http://rubyer.me/blog/941/ ... -
mysql配置
2012-03-20 22:36 840http://wiisola.iteye.com/blo ... -
Gem 淘宝mirror地址
2012-03-19 12:47 847https://ruby.taobao.org -
搭建 Nginx 反向代理,提高 Gem 的安装速度
2012-03-19 12:43 757最近时常遇到 Gem 安装东西的时候安装失败或异常缓慢, ... -
细说Ruby工程的Bundle以及如何加快bundle install
2012-03-19 12:33 687做过Ruby项目的人可能有过我一样的感受,rubygem ... -
Gems淘宝镜像
2012-03-04 23:09 0Shell代码 $ ... -
学习笔记
2012-03-04 11:55 517Command LIne Switches: -
Ruby待解决
2012-02-21 21:47 5771.inject 2.|x|这种用法 -
Tutor
2012-02-19 16:14 472http://www.fincher.org/tips/Lan ... -
Tutorial
2012-02-19 12:46 630Variable naming Ok, let's slo ...
相关推荐
《Ruby程序设计语言》是Ruby的权威指南,全面涵盖该语言的1.8版和1.9版。本书详尽但并不拘泥于语言规范,既适合首次接触Ruby的资深程序员,同样也适合... Method、proc、lambda和closure 类和模块 反射和元编程
闭包编译器(作为Ruby Gem) 闭合编译器gem是用于JavaScript压缩的的精巧包装。 最新版本: gem包含Closure Compiler的2018-05-06 JAR文件。安装sudo gem install closure-compiler用法Closure::Compiler有一个...
使用Ruby 2.5和2.6针对ActiveRecord 4.2、5.0、5.1、5.2和6.0进行了测试 支持抚养孩子(及其所有后代) 支持层次结构内的 find_or_create_by_path用于 支持 支持后代的遍历 支持使用以渲染树 在各种环境中的出色 ...
Closure Tree - 轻松高效地使你的ActiveRecord模型支持层次结构
闭包(Closure),是指未绑定到任何对象的自由代码,闭包中的代码与任何对象和全局变量无关,只与执行此段代码的上下文相关。 今天我们简要的看一下ruby中的闭包实现。 Ruby中的闭包实现有:Block,Proc,Lambada。 ...
Backbone.js externs 与以下版本的Google Closure编译器一起使用的Backbone.js的Externs文件: 0.9.2 0.9.10(RC为1.0) 1.0.0 1.1.0 这是一项正在进行中的工作,尽管应该...确保已安装Ruby export CC_PATH={pat
. ruby . Io . Erlang . Scala . Closure . Scala . Haskell
您可能需要Java作为Closure javascript压缩程序(如果没有它,请向我发送消息)。 是处理多个的好工具。 WebSync已针对Ruby 2.0.0和Ruby 2.1.2进行了自动测试。 除了前面提到的依赖关系之外,还需要一些全局依赖...
运行测试在浏览器中:转到tests/index.html 无头测试: npm install && npm test使生产版本缩小您将需要ruby和Google Closure编译器: gem install closure-compiler 。 然后,只需调用: ./bin/build.sh version ,...
自述 此自述文件通常会记录... Ruby版 系统依赖 配置 数据库创建 数据库初始化 如何运行测试套件 服务(作业队列、缓存服务器、搜索引擎等) 部署说明 … 如果您不打算运行rake doc:app请随意使用不同的标记语言。
clover2计算机语言该语言...2. 関数型言語のようにLambdaやclosureは第一級オブジェクトです。正規表現も第一級オブジェクトです。3. 簡易なGenericsがあります。JavaのGenericsを簡素にしたようものです。コンパイル
空中花园NoRIA 网页的 JavaScript 项目结构。哲学为了使基于 JavaScript 的 RIA 开发更容易,我们付出了... 然后可以通过 Googles Closure Compiler 或 JSMin 进一步优化此文件。它是如何工作的一个新的 Hanging Garde
Thinreports编辑器是Ruby的开源报告生成工具。 Thinreports编辑器(GUI设计器) Thinreports生成器(Ruby的报表生成器)产品特点Generator的功能。易于使用较少的特殊培训,可以通过拖放操作创建。多平台当前支持的...
Google Closure Compiler 用于 JS 和 YUI Compressor for CSS。 Jade -> HTML 文件会自动收缩包装。 链轮用于文件请求和连接,因此可以使用所有相同的链轮语法。 我创建它是为了帮助 PhoneGap/Cordova 开发,它的...
百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对...
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!...
业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。目前互联网很多服务如Open API,很多大头公司如Google,Yahoo,Microsoft等都提供了OAUTH...
1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6 1.3.2 Java程序的运行机制和JVM 6 1.4 开发Java的准备 7 1.4.1 安装JDK 8 学生提问:不是说JVM是...
业界提供了OAUTH的多种实现如PHP,JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。目前互联网很多服务如Open API,很多大头公司如Google,Yahoo,Microsoft等都提供了OAUTH...