理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods
这可能是因为Ruby处理闭包的方式有点怪。更甚的是,Ruby有4种处理闭包的方式, 第一次用,每种都不太顺手。
首先:blocks代码块
array = [1, 2, 3, 4]
array.collect! do |n|n ** 2
end
puts array.inspect
# => [1, 4, 9, 16]
collect!是Ruby库里的方法,下面我们来写一个自己的类似方法iterate!
class Arraydef iterate!self.each_with_index do |n, i|self[i] = endend
end
array = [1, 2, 3, 4]
array.iterate! puts array.inspect
# => [1, 4, 9, 16]
整个调用如下:
1、一个整数组成的数组调用iterate!
2、当yield被调用时,把n(第一次为1,第二次为2,…)传给block
3、block对n进行n**2。因为是最后一行,自动作为结果返回。
4、yield得到block的结果,并把值重写到array里。
5、数据中每个对象执行相同操作。
class Arraydef iterate!(&code)self.each_with_index do |n, i|self[i] = code.call(n)endend
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
Procs 过程
block与Proc惟一的不同是:block是不能保存的Proc,一性的。
class Arraydef iterate!(code)self.each_with_index do |n, i|self[i] = code.call(n)endend
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]
而block是Ruby特有的方式。
另外Ruby不只使用blocks做闭包还有一个原因。比如有时我们需要传递多个闭包给一个方法,这时block马上力不从心了。但我们可以用Proc:
def callbacks(procs)procs[:starting].callputs "Still going"procs[:finishing].call
end
callbacks(:starting => Proc.new { puts "Starting" },:finishing => Proc.new { puts "Finishing" })
# => Starting
# => Still going
# => Finishing
1、Block:方法把一个对象拆分成很多片段,并且你希望你的用户可以与这些片段做一些交互。
2、Block:希望自动运行多个语句,如数据库迁移(database migration)。
3、Proc:希望多次复用一段代码。
4、Proc:方法有一个或多个回调方法(callbacks)。
为什么block小写,而Proc大写
这只是我个人习惯。因为Proc是Ruby中的一个类似,而blocks并没有自己的类(本质上只是Procs),只是一种语法规则。后面的lambda 小写也是如此。
Lambda 拉姆达表达式
class Arraydef iterate!(code)self.each_with_index do |n, i|self[i] = code.call(n)endend
end
array = [1, 2, 3, 4]
array.iterate!(lambda { |n| n ** 2 })
puts array.inspect
# => [1, 4, 9, 16]
1、lambdas检查参数的个数,Procs不会。
def args(code)one, two = 1, 2code.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)
2、return不同。lambdas的return是返回值给方法,方法会继续执行。Proc的return会终止方法并返回得到的值。有点拗口,下面看例子。
def proc_returnProc.new { return "Proc.new"}.callreturn "proc_return method finished"
end
def lambda_returnlambda { return "lambda" }.callreturn "lambda_return method finished"
end
puts proc_return
puts lambda_return
lambda_return中,执行到lambda中的return时,返回”lambda”,方法继续执行。
答案在于procedures和methods概念上的不同。
Ruby中的Procs是代码片段(code snippets),不是方法。因此,Proc的return就是整个方法的return。
但lambdas就像是单独的methods(只不过是匿名的),所以它要检查参数个数,且不会覆盖整个方法的返回。
因此,最好把lambdas当作另一种methods的写法,一种匿名的方式。
def generic_return(code)code.callreturn "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
还可以参考:
def generic_return(code)one, two = 1, 2three, 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
Method 对象
class Arraydef iterate!(code)self.each_with_index do |n, i|self[i] = code.call(n)endend
end
def square(n)n ** 2
end
array = [1, 2, 3, 4]
array.iterate!(method(:square))
puts array.inspect
# => [1, 4, 9, 16]
def square(n)n ** 2
end
总结
blocks和Procs看起来像在代码中插入代码片段。而lambdas和Methods看起来像方法。
转载于:https://blog.51cto.com/flyingsnail/1088024
理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods相关推荐
- 深入理解Blocks,Procs和lambdas
首发在:[url]http://blog.enjoyrails.com/?p=125[/url] 原文:[url]http://www.robertsosinski.com/2008/12/21/un ...
- 面试官:谈谈对JS闭包的理解及常见应用场景(闭包的作用)
文章目录 对JS闭包的理解及常见应用场景(闭包的作用) 1.变量作用域 2.如何从外部读取函数内部的变量? 3.闭包概念 4.闭包用途 5.闭包的理解 6.闭包应用场景 setTimeout传参 回调 ...
- 快速理解VirtualBox的四种网络连接方式
转自:http://www.cnblogs.com/york-hust/archive/2012/03/29/2422911.html VirtualBox中有4中网络连接方式: NAT Bridge ...
- 理解JS的6种继承方式
[转]重新理解JS的6种继承方式 写在前面 一直不喜欢JS的OOP,在学习阶段好像也用不到,总觉得JS的OOP不伦不类的,可能是因为先接触了Java,所以对JS的OO部分有些抵触. 偏见归偏见,既然面 ...
- Ubuntu系统安装Ruby的三种方法
Ubuntu系统安装Ruby的三种方法 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs Ruby是一个开源的动态编程语言,它有优美的语法,可用于构建可伸缩 ...
- 三分钟理解Python函数式编程与闭包
函数式编程 函数式编程这个概念我们可能或多或少都听说过,刚听说的时候不明觉厉,觉得这是一个非常黑科技的概念.但是实际上它的含义很朴实,但是延伸出来许多丰富的用法. 在早期编程语言还不是很多的时候,我们 ...
- 谷歌AI论文BERT双向编码器表征模型:机器阅读理解NLP基准11种最优(公号回复“谷歌BERT论文”下载彩标PDF论文)
谷歌AI论文BERT双向编码器表征模型:机器阅读理解NLP基准11种最优(公号回复"谷歌BERT论文"下载彩标PDF论文) 原创: 秦陇纪 数据简化DataSimp 今天 数据简化 ...
- 深入理解MySQL的四种隔离级别
深入理解MySQL的四种隔离级别[日期:2017-02-20] 来源:Linux社区 作者:Linux [字体:大 中 小]什么是事务事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个 ...
- 深入理解C++中五种强制类型转换的使用场景
深入理解C++中五种强制类型转换的使用场景 1.C风格的强制类型转换 2.C++风格的强制类型转换 2.1.static_cast 2.1.1.用于基本内置数据类型之间的转换 2.1.2.用于指针之间 ...
- 举例理解Hibernate的三种状态:瞬时态、持久态、托管(即游离态)态及互相转化
举例理解Hibernate的三种状态 初学Hibernate,了解到Hibernate有三种状态:transient(瞬时状态),persistent(持久化状态)以及detached(游离状态). ...
最新文章
- 滚动时域控制 matlab,在 Simulink 中设计神经网络预测控制器
- kafka_2.11-0.10.2.1中的auto.offset.reset
- php改变iframe的src,js动态改变iframe的src属性
- ESXi 内存分配原理
- java面试要点---Spring体系知识点复习,IOC,AOP---随时更新
- idea java 快捷键_图示Javahtml5开发中IDEA的一些常用默认快捷键
- mysql多表查询练习_MySQL多表查询综合练习答案
- JavaScript 遗漏知识再整理;错误处理,类型转换以及获取当前时间、年份、月份、日期;...
- Debugging with GDB 用GDB调试多线程程序
- mysql安装包下载与检核
- 「企业架构」TOGAF的权威指南
- python 手机号码归属地 软件,Python查询手机号码归属地几种方法
- edp和edt哪个好_香水edt和edp是什么意思
- 未来科技蒲公英大飞_大烟草的下跌告诉我们关于大科技的未来
- C++ 全局变量 静态全局变量 傻傻分不清
- vscode下载和前端工程师常用的20+插件,包含代码提示、语法高亮、括号颜色等等
- GlusterFS探究(一): dht,afr,fuse, mgmt 层 几个问题总结
- 解决中文乱码问题:使用编码转换工具
- 【安卓逆向】 浦X银行签名校验,逆向工程师绝不认输
- PaaS平台如何实现简化应用开发和部署