Ruby 关于 Proc 和 lambda 的一点疑惑

blabber · 2014年01月20日 · 最后由 ensonmj 回复于 2014年01月20日 · 2936 次阅读

关于两者的差异,有篇文章提到: “lambda 和 Proc 是一样的,除了 Proc 的 return 会跳出调用的方法,lambda 则不会,它只是返回自己。” 下面的代码证明了这段话的正确。

def foo
  f = Proc.new { return "return from foo from inside proc" }
  f.call # control leaves foo here
  return "return from foo"
end

def bar
  f = lambda { return "return from lambda" }
  puts f.call # control does not leave bar here  prints "return from lambda"
  return "return from bar"
end

puts foo # prints "return from foo from inside proc"
puts bar # prints "return from bar"

======= 但是,我验证下面的代码,就感觉有些疑惑了。

def call_block(&block)
  block.call(1)
  block.call(2)
  block.call(3)
end

proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!" }
proc_2 = lambda { |i| puts "#{i}: Blocks are cool!" }

call_block(&proc_1)
call_block(&proc_2)

输出:
1: Blocks are cool!
2: Blocks are cool!
3: Blocks are cool!
1: Blocks are cool!
2: Blocks are cool!
3: Blocks are cool!

从上面的输出可见,不管是 proc 或者 lambda 均顺序执行玩三个 call 并输出了结果,而没有退出? 疑惑就是,按第一段代码的逻辑,call_block(&proc_1) 应该在执行完 block.call(1) 后退出方法才对,怎解?谢谢!

lambda 与 Proc 的一大区别是前者检查传入参数是否符合,后者不检查:

lambda { |p| }.()
ArgumentError: wrong number of arguments (0 for 1)

Proc.new { |p| }.()
=> nil

关于 return 的问题试验一下再来 update。

根据 @ghjcumt2008 9 个月前的一个总结说“lambda 会执行 return,Proc 遇到 return 会中断”,我修改代码如下。

def call_block(&block)
  block.call(1)
  return "1"
  block.call(2)
  return "2"
  block.call(3)
  return "3"
end

proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!" }
proc_2 = lambda { |i| puts "#{i}: Blocks are cool!" }

call_block(&proc_1)
call_block(&proc_2)

输出
1: Blocks are cool!
1: Blocks are cool!

从输出看,不管是 proc 还是 lambda,在执行完 block.call(1) 后,都进行了中断。

谢谢 @ashchan 之前 @ghjcumt2008 做了个总结,我觉得挺全面。翻出来,供大家参考!

总结 1.block 和 Proc 都是代码块,Proc 可以复用,block 不可以复用 2.block 和 lambda 不是类,Proc 是类,所以 block 和 lambda 是小写,Proc 是大写 3.lambda 是匿名函数 4.lambda 会对参数个数验证,Proc 不会验证 5.lambda 会执行 return,Proc 遇到 return 会中断

你的 proc 里面貌似没有 return

#3 楼 @blabber 如 4 楼所说,这里的区别是针对在 lambda 或 Proc 内部的显示 return 而言,如 proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!"; return }

谢谢! @ytwman @ashchan 但是在第一段代码里面,return 也不是在 proc 或 lambda 的里面啊?

哦,我明白了。里面也有 return,只不过关注了后面的 return。 谢谢两位啦!

#6 楼 @blabber 这里的关键是,第一段代码里的 return "return from foo" 是不会跑到的。

区别在元编程那本书里面其实已经说了。挺好理解的

@AlphaLiu 谢谢!倒是有这本书,还没有来得及看。谢谢你!

其实我觉得 proc 可以类比为 c 语言中的宏,lambda 则是内联函数

需要 登录 后方可回复, 如果你还没有账号请 注册新账号