Procs and Lambdas(2)

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看成类似方法的东西。

 

你可能感兴趣的:(lambda)