Proc是对块及其context(局部变量的作用域以及栈框架)进行对象化处理之后得到的过程对象。您可以像使用无名函数那样来使用Proc,但它不会导入局部变量的作用域(可以把动态局部变量用作Proc局部变量)。
在下例中,正因为Proc一直保持着局部变量的作用域,所以才能调用var变量。
1
2
3
4
5
6
7
8
9
|
var = 1
$foo = Proc . new { var }
var = 2
def foo
$foo .call
end
p foo # => 2
|
从生成Proc的方法中返回以后,若Proc中出现return或retry的话,会引发LocalJumpError异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def foo
proc { return }
end
foo.call
# => in `call': return from proc-closure (LocalJumpError)
def foo
proc { retry }
end
foo.call
# => in `call': retry from proc-closure (LocalJumpError)
|
若在Proc前面加上"&"并将其传给一个带块的方法时,其运作情形类似于调用块。但从严格意义上讲,其间还存在以下不同。
1
2
3
4
5
6
7
8
9
10
11
12
|
# 没问题
( 1 .. 5 ). each { break }
# 在ruby 1.6.7, 1.8中没问题。在1.6.8中则发生异常
proc = Proc . new { break }
( 1 .. 5 ). each (&proc)
# 在ruby 1.6 中是 LocalJumpError
# 在ruby 1.8 中,再次运行each
proc = Proc . new { retry }
( 1 .. 5 ). each (&proc)
#=> retry from proc-closure (LocalJumpError)
|
这正是Proc对象用作调用块时的限制。
1
2
|
Proc . new
Proc . new { ... }
|
对块及其context进行对象化处理之后返回结果。
若没有给出块的话,就会把调用该方法的方法所带的块转换为Proc对象并将其返回。
1
2
3
4
5
6
|
def foo
pr = Proc . new
pr.call( 1 , 2 , 3 )
end
foo {|args| p args }
# => [1, 2, 3]
|
Proc.new方法深入
Proc.new对块及其context进行对象化处理之后返回结果。
若没有给出块的话,就会把调用该方法的方法所带的块转换为Proc对象并将其返回。
1
2
3
4
5
6
7
8
9
10
11
12
|
def foo
pr = Proc . new
pr.call( 1 , 2 , 3 )
end
foo {|args| p args }
# => [1, 2, 3]
这与下例相同
def foo
yield ( 1 , 2 , 3 )
end
foo {|args| p args }
# => [1, 2, 3]
|
若主调方法并没有带块时,则引发ArgumentError异常。
1
2
3
4
5
6
7
|
def foo
Proc . new
end
foo
# => -:2:in `new': tried to create Proc object without a block (ArgumentError)
from -: 2 :in `foo'
from -: 4
|
在使用Proc.new时,若定义了Proc#initialize方法的话,就在对象初始化时调用该方法。除此以外,它和proc是相同的。
利用 Proc.new 方法,或者对 proc 方法指定块,都可以创建代表块的 Proc 对象。
通过调用 Proc#call 方法执行块。调用 Proc#call 方法时的参数会作为块变量,块中最后一个表达式的值则为 Proc#call 的返回值。Proc#call 还有一个名称叫 Proc#[]。
1
2
3
4
5
6
7
8
|
# 判断西历的年是否为闰年的处理
leap = Proc . new do |year|
year % 4 == 0 && year % 100 != 0 || year % 400 == 0
end
p leap.call( 2000 ) #=> true
p leap[ 2013 ] #=> false
p leap[ 2016 ] #=> true
|
将块变量设置为 |* 数组 | 的形式后,就可以像方法参数一样,以数组的形式接收可变数量的参数。
1
2
3
4
5
6
|
double = Proc . new do |*args|
args.map{|i| i * 2 } # 所有元素乘两倍
end
p double.call( 1 , 2 , 3 ) #=> [2, 3, 4]
p double[ 2 , 3 , 4 ] #=> [4, 6, 8]
|
除此以外,定义普通方法时可使用的参数形式,如默认参数、关键字参数等,几乎都可以被用于块变量的定义,并被指定给 Proc#call 方法。