在ruby中以do...end包括的内容为block(代码块),如果只有一行可以用{...}.block无法单独存在,通常需要结合method使用.
比如平时使用最多的数组循环:
# 一个简单的迭代代码
[1,2,3,4,5].each do |item|
p item
end
# 还可以这样子写
[1,2,3,4,5].each { |item| p item }
yield
在调用方法的时候,可以传递参数.有时候需要把一个代码块当作参数进行传递.这种时候可以使用yield实现.yield相当于占位符的概念,先把某一个代码过程中的位置占据,等待方法执行的时候根据传递的内容进行处理.
比如想统计一个很耗时的操作执行了多少时间,可以这样写:
# 时间方法执行间隔
def time_interval
p "开始时间为:#{Time.now}"
yield # 默认不需要参数
p "结束时间为:#{Time.now}"
end
# 设置不同的时间间隔
time_interval { sleep(3) }
time_interval { sleep(5) }
执行结果
# 可以看到执行了以上代码之后,会执行sleep操作
"开始时间为:2018-04-04 09:52:37 +0800"
"结束时间为:2018-04-04 09:52:40 +0800"
"开始时间为:2018-04-04 09:52:40 +0800"
"结束时间为:2018-04-04 09:52:45 +0800"
可以看到上面的代码中,在调用time_interval方法的时候传递一个代码块作为一个默认参数,在方法内部使用yield把代码块放置在指定的位置.
通过yield调用方法的时候,可以传递参数.如:
def sum
yield 3,5
end
sum { |a, b| p "#{a}+#{b}=#{a + b}" }
执行结果
"3+5=8"
proc
被调用的方法在需要传递参数的时候,代码块作为参数时,需要放在所有参数的最后面.在参数名字前面加上&符号,表示是一个代码片段参数.在调用的时候通过调用参数的call方法执行,如:
def log_msg(type, &b)
p "当前的信息类型为#{type}"
[1, 2, 3, 4].each do |item|
b.call(item)
end
end
myproc = proc { |t| p "执行了#{t}次" }
log_msg('写操作日志', &myproc)
执行结果
"当前的信息类型为写操作日志"
"执行了1次"
"执行了2次"
"执行了3次"
"执行了4次"
这里用到了关键字proc.使用block代码块的时候无法重复使用,代码块只能跟在方法调用后面.通过proc可以把需要执行的代码块进行保存,存为变量方面重复使用.
#
myproc = proc { |t| p "执行了#{t}次" }
# 也可以这样子定义
myproc = Proc.new { |t| p "执行了#{t}次" }
yield与block
通过上面的例子可以发现在使用代码块作为参数的时候,两者都可以用.下面通过一些简单的分析了解一下两者的区别
# 调用yield相当与调用block.call方法
def method_with_block(&block)
p block.class
yield
end
method_with_block { p '我在方法的块内' }
my_proc = Proc.new do
p "这是定义的一个Proc实例"
p "看下输出结果如何"
end
method_with_block &my_proc # 注意传递参数的时候一定要加&,否则会报错
执行结果
Proc # 方法块的class为Proc
"我在方法的块内" #
lambda
除了上面提到的两种写法之外还有lambda这种写法.
def arr_test(&l)
# p l.class
[1, 2, 3, 4].each do |item|
yield item
end
end
lambda1 = lambda { |n| p n*2 }
arr_test &lambda1
执行结果
2
4
6
8
上面的lambda可以写成如下的形式
lambda1 = lambda { |n| p n*2 }
# 或者这种写法
lambda1 = ->(n) { p n*2 }
通过输出lambda的class发现也是Proc
Proc
通过上面的一系列代码和例子可以发现,他们的class都是Proc对象,可以通过不同的方式进行定义.Proc 对象的定义有几种形式:
- 直接使用 {}
- 使用 Proc.new {}
- 使用 proc {}
- 使用 lambda {}