一: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)
=> 32.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
=> 3todo: 在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
=> 6todo: 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')
=> 11binding可以获得当前作用域内部的变量,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
随时随地看视频