ruby中的可调用对象--proc和lamdba

  ruby中将块转变成对象的三种方法

  ruby中的大部分东西都是对象,但是块不是。那么,如果你想存下来一个块,方便以后使用,你就需要一个对象。ruby中有三种方法,把块转换成可以利用的对象。

  1. Proc.new   
  2. lambda          #kernel的方法
  3. proc      #等价于Proc.new

这三种很类似,如下:

1 inc  = Proc.new{|x|x+1}
2 p inc.call(1)
3 
4 inc = proc{|x|x+1}
5 p inc.call(1)
6 
7 dec = lambda{|x|x-1}
8 p dec.class
9 p dec.call(1)

输出

2
2
Proc
0


 

  ruby中把Proc对象转变成块

  说完了把块变成对象,那么怎么把对象再变成块呢?那就要用到&操作符

  先说一下ruby中调用块的方法

  ruby有一个yield关键字,在方法用yield语句可以调用一个块。

如下:

1 def square
2     yield(2)
3 end
4 square{|x|puts "#{x*x}"}

输出

4

  方法中使用了yield语句,带有参数2,然后在调用方法时,带着一个块,块中也有一个参数x。执行到yield语句的时候,把块代进去执行。

 

  但是用yield有两种情况下不适用:

  • 想把这个块传给另一个方法
  • 想把这个块转换成一个Proc

  这两种情况下,都要把块取一个名字。通过这个名字来进行调用块。这个块做参数的时候,必须是参数列表的最后一个,并且以&开头。

如下:

1 def square
2     yield(2)
3 end
4 def show(&block)
5     square(&block)
6 end
7 show{|x|puts "#{x*x}"}

输出

4

  如例子所示,在调用show的时候,如果没有加一个块,&block就会是nil。那么在调用square的时候将失败。

  &的含义:&表示紧接着的变量是一个Proc对象,但是要把它当成一个块来使用。如果去掉&,就是再次得到Proc对象。

如下:

1 def method(&block)
2     block
3 end
4 
5 a = method{p "hello world"}
6 p a.class

输出

Proc 


 

Proc.new(proc)和lambda的区别

  Proc.new和lambda有两个重要的区别:一个和return关键字有关,一个和参数检验有关。

  在return上的不同

 1 def square
 2     p = Proc.new{|x|return x}
 3     result = p.call(2)
 4   return result*result
 5 end
 6 p square
 7 
 8 def square1
 9     l = lambda{|x|return x}
10     result = l.call(2)
11     return result*result
12 end
13 p square1

输出2 4

  Proc.new是从定义proc的作用域返回——如上示例,Proc定义在square方法中,因此Proc.new中定义的return x,是作为这个方法的返回结果。最后一句return result*result没有执行。

  lamdba是从仅仅从lambda中返回。和我们正常的逻辑一样。返回result =2 ,然后再执行最后一句。

 

  检查参数方式不同

如下:

1 p = Proc.new{|a,b|[a,b]}
2 p p.call(1,2)
3 p p.call(1,2,3)
4 p p.call(1)
5 
6 l = lambda{|a,b|[a,b]}
7 p l.call(1,2)
8 p l.call(1,2,3)
9 p l.call(1)

输出

[1, 2]
[1, 2]
[1, nil]
[1, 2]
a.rb:6:in `block in <main>': wrong number of arguments (3 for 2) (ArgumentError)

from a.rb:8:in `call'
from a.rb:8:in `<main>'

  如输出结果所示,Proc.new在参数数目不对的时候会自动调整,而lambda会报错。

 


 

总结:在ruby调用一个方法时,可以选择是否提供一个代码块,这样可以让方法匿名调用该代码块,用yield。

     Proc.new(proc)和lamdba方法都可以把块转化成Proc对象,但是选择的时候一般优先lambda。因为lambda对参数数量要求严格,而且调用return只从代码中返回。除非要用到Proc.new的一些特殊功能。

   可以用Proc#lambda?()方法来检测Proc是不是lambda。 

posted @ 2014-07-30 15:42  smallbottle  阅读(339)  评论(0编辑  收藏  举报