proc是block的对象形式,它的行为像是一个block,lambda和proc之间有些细小的差别,lambda更像一个方法,而不是一个block,调用一个proc就像yield一个block,调用一个lambda就像在调用一个方法(当然还是有区别的),在ruby1.9里,你可以使用lambda?来查看这个对象究竟是一个lambda还是一个proc。下面,是proc和lambda的一些区别:
1,Return in blocks,procs,and lambdas
在一个block或proc中执行return,程序将从包含这个block或proc的方法中返回
def test puts "entering method" p = Proc.new { puts "entering proc"; return } p.call # Invoking the proc makes method return puts "exiting method" # This line is never executed end test #=》 # entering method # entering block
在一个lambda中执行return,只是从该lambda中返回,在这也可以看出lambda像个方法
def test puts "entering method" p = lambda { puts "entering lambda"; return } p.call # Invoking the lambda does not make the method return puts "exiting method" # This line *is* executed now end test #=》 # entering method # entering lambda # exiting method
2,break in blocks,procs,and lambdas
在一个block或proc中调用break,会使用程序从block或proc中返回到它的iterator,再从它的iterator返回到调用它的方法中,当我们使用Proc.new创建一个proc时,Proc.new是一个iterator,我们使用break,程序将会从这个iterator中返回。这时我们调用这个proc对象,程序已经从它的iterator已经返回了,在一个Proc.new中调用break是没有什么意义的
irb(main):048:0> proc = Proc.new { puts "entering proc"; break } => #<Proc:0x8a83984@(irb):48> irb(main):049:0> proc.call entering proc LocalJumpError: break from proc-closure from (irb):48:in `block in irb_binding' from (irb):49:in `call' from (irb):49 from /usr/local/bin/irb:12:in `<main>' #有点难理解
下一个例子,比较好理解:
def iterator(&proc) puts "entering iterator" proc.call # invoke the proc puts "exiting iterator" # Never executed if the proc breaks end def test iterator { puts "entering proc"; break } end test #=》 # entering iterator # entering proc
在lambda中调用break效果和return是一样的
3,Other control-flow statements
next: 在block,proc,lambda中表现是一样的,都是从本个yield或都是call中返回到iterator,如果next 后面跟一个表达式,该表达式的值将被作为返回值返回
irb(main):052:0> proc = Proc.new { puts "entering proc"; next } => #<Proc:0x89bf070@(irb):52> irb(main):053:0> proc.call entering proc => nil irb(main):054:0> proc = Proc.new { next;puts "entering proc" } => #<Proc:0x89b58f4@(irb):54> irb(main):055:0> proc.call => nil irb(main):056:0> a=[1,2,3] => [1, 2, 3] irb(main):057:0> a.each{|x|next if x == 2;puts x } 1 3 => [1, 2, 3]
redo:一样
retry:不能用在porcs和lambdas中,会产生LocalJumpError
raise:如果在block,proc,lambda中raise一个exception,并且不在该 block,proc和lambda中捕捉异常,异常会传播到调用该block,proc,lambda的方法中。
procs和lambdas的参数传递:这是一个意外收获~
使用yield调用一个block(使用call()调用proc)和调用一个方法非常相似,但是它们是不一样的,
yield语句使用 yield semantics,它的语法和并行赋值一样,
方法调用使用 invocation semantics .它没并行赋值那么强大,lambda类似方法,所以它也是采用这个方法调用。
p = Proc.new {|x,y| print x,y } p.call(1) # x,y=1: nil used for missing rvalue: Prints 1nil p.call(1,2) # x,y=1,2: 2 lvalues, 2 rvalues: Prints 12 p.call(1,2,3) # x,y=1,2,3: extra rvalue discarded: Prints 12 p.call([1,2]) # x,y=[1,2]: array automatically unpacked: Prints 12 #yield semantics 一切都是自动的,抛弃多余的实参,为没有值的形参赋 #nil,甚至自动打散数组,这在方法(或lambda)中都是需要显示的调用*才行的 l = lambda {|x,y| print x,y } l.call(1,2) # This works l.call(1) # Wrong number of arguments l.call(1,2,3) # Wrong number of arguments l.call([1,2]) # Wrong number of arguments l.call(*[1,2]) # Works: explicit splat to unpack the array
总结:proc和lambda在大部分情况下表现是一样的,当遇到return ,break等涉及到控制权变更时才会有些许的不一样,一个简便的方法就是把proc看成类似block的东西,而lambda看成类似方法的东西。