为了账号安全,请及时绑定邮箱和手机立即绑定

ruby proc/lambda/method/block

标签:
Ruby

一:proc / lambda

1.让我们分别以proc和lambda两种方式来定义代码块

2.2.2 :001 > a = proc { |x| x }
 => #<Proc:0x007fe62c31d610@(irb):1>2.2.2 :002 > a.call(2)
 => 22.2.2 :003 > b = lambda { |x| x }
 => #<Proc:0x007fe62c26fc68@(irb):3 (lambda)>2.2.2 :004 > b.call(3)
 => 3

2.proc和lambda有什么区别呢

(1)proc是一个代码块,而lambda更像一个方法

2.2.2 :005 > a.call
 => nil2.2.2 :006 > b.callArgumentError: wrong number of arguments (0 for 1)
    from (irb):3:in `block in irb_binding'
    from (irb):6:in `call'
    from (irb):6
    from /Users/tnt29/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>'

todo: proc不传递任何参数也是可以的,返回nil,而lambda不传递参数就会报错
(2)

2.2.2 :007 > def method_a2.2.2 :008?>   a = proc { return 3 }2.2.2 :009?>   a.call2.2.2 :010?>   42.2.2 :011?>   end
 => :method_a2.2.2 :012 > method_a
 => 3

todo: 在method_a这个方法内部,proc做为整个方法体直接返回了

2.2.2 :013 > def method_b2.2.2 :014?>   b = lambda { return 5 }2.2.2 :015?>   b.call2.2.2 :016?>   62.2.2 :017?>   end
 => :method_b2.2.2 :018 > method_b
 => 6

todo: lambda把方法内都执行完毕了

总结:proc和lambda最主要的区别是:
lambda的行为和传统意义的ruby的method方法的性质的是一致的,因为上面无论是参数的区别还是返回值的区别,都是和一个正常的函数保持一致的。
而proc严格意义上是一个代码块,改变当前作用的上下文的解释顺序的

二:method / block

如何将一个方法做为参数传递呢?

2.2.2 :019 > method(:method_a).call
 => 3

比如面试官为了考验你对代码块的理解给你出了一个面试题,你怎么定义一个方法来实现计数器
实现代码如下:

2.2.2 :021 >   def count2.2.2 :022?>   a = 02.2.2 :023?>   proc { a+=1 }2.2.2 :024?>   end
 => :count2.2.2 :025 > x = count
 => #<Proc:0x007fe62be06eb0@(irb):23>2.2.2 :026 > x.call
 => 12.2.2 :027 > x.call
 => 22.2.2 :028 > x.call
 => 32.2.2 :029 > x.call
 => 42.2.2 :030 >

这是个什么原理呢?
因为代码块是可以保留它所在方法的作用域的,当然也就代表保留了上下文作用域的变量。

其实在ruby中有一个方法叫binding是返回作用域的

2.2.2 :031 >   def hello2.2.2 :032?>   y = 102.2.2 :033?>   end
 => :hello2.2.2 :034 > hello
 => 102.2.2 :035 > yNameError: undefined local variable or method `y' for main:Object
    from (irb):35
    from /Users/tnt29/.rvm/rubies/ruby-2.2.2/bin/irb:11:in `<main>'

我们加上binding试一下

2.2.2 :063 > def hello2.2.2 :064?>   y = 112.2.2 :065?>   binding2.2.2 :066?>   end
 => :hello2.2.2 :067 > m = hello
 => #<Binding:0x007fe62d0de448>2.2.2 :068 > m.eval('y')
 => 11

binding可以获得当前作用域内部的变量,DSL中经常用到。

最后再说说block,我们经常遇到,就是do..  end
代码接受外部代码块有以下两种:

#命名代码块2.2.2 :001 > def method_c &block2.2.2 :002?>   block.call2.2.2 :003?>   end
 => :method_c2.2.2 :004 > method_c { 9 }
 => 9#匿名代码块2.2.2 :005 > def method_d2.2.2 :006?>   yield2.2.2 :007?>   end
 => :method_d2.2.2 :008 > method_d { 10 }
 => 10

这两种方法的性能如何呢,用ruby内置的Benchmark模块来看下两个的运行时间
我们执行100万次

2.2.2 :012 > require 'benchmark'
 => false2.2.2 :013 > Benchmark.realtime { 1_000_000.times { method_c { 9 } } }
 => 0.75586128805298362.2.2 :014 > Benchmark.realtime { 1_000_000.times { method_d { 9 } } }
 => 0.1121725169941783

这里会发现0.75秒(命名代码块)  第二个0.1秒(匿名代码块)
匿名代码块 效率远远高于命名代码块
所以在开发中我们尽量选择后者



作者:程序员小哥哥
链接:https://www.jianshu.com/p/fea1c8d27087

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消