一:proc / lambda
1.让我们分别以proc和lambda两种方式来定义代码块
2.2.2 :001 > a = proc { |x| x }
=> #<Proc:0x007fe62c31d610@(irb):1>
2.2.2 :002 > a.call(2)
=> 2
2.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
=> nil
2.2.2 :006 > b.call
ArgumentError: 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_a
2.2.2 :008?> a = proc { return 3 }
2.2.2 :009?> a.call
2.2.2 :010?> 4
2.2.2 :011?> end
=> :method_a
2.2.2 :012 > method_a
=> 3
todo: 在method_a这个方法内部,proc做为整个方法体直接返回了
2.2.2 :013 > def method_b
2.2.2 :014?> b = lambda { return 5 }
2.2.2 :015?> b.call
2.2.2 :016?> 6
2.2.2 :017?> end
=> :method_b
2.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 count
2.2.2 :022?> a = 0
2.2.2 :023?> proc { a+=1 }
2.2.2 :024?> end
=> :count
2.2.2 :025 > x = count
=> #<Proc:0x007fe62be06eb0@(irb):23>
2.2.2 :026 > x.call
=> 1
2.2.2 :027 > x.call
=> 2
2.2.2 :028 > x.call
=> 3
2.2.2 :029 > x.call
=> 4
2.2.2 :030 >
这是个什么原理呢?
因为代码块是可以保留它所在方法的作用域的,当然也就代表保留了上下文作用域的变量。
其实在ruby中有一个方法叫binding是返回作用域的
2.2.2 :031 > def hello
2.2.2 :032?> y = 10
2.2.2 :033?> end
=> :hello
2.2.2 :034 > hello
=> 10
2.2.2 :035 > y
NameError: 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 hello
2.2.2 :064?> y = 11
2.2.2 :065?> binding
2.2.2 :066?> end
=> :hello
2.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 &block
2.2.2 :002?> block.call
2.2.2 :003?> end
=> :method_c
2.2.2 :004 > method_c { 9 }
=> 9
#匿名代码块
2.2.2 :005 > def method_d
2.2.2 :006?> yield
2.2.2 :007?> end
=> :method_d
2.2.2 :008 > method_d { 10 }
=> 10
这两种方法的性能如何呢,用ruby内置的Benchmark模块来看下两个的运行时间
我们执行100万次
2.2.2 :012 > require 'benchmark'
=> false
2.2.2 :013 > Benchmark.realtime { 1_000_000.times { method_c { 9 } } }
=> 0.7558612880529836
2.2.2 :014 > Benchmark.realtime { 1_000_000.times { method_d { 9 } } }
=> 0.1121725169941783
这里会发现0.75秒(命名代码块) 第二个0.1秒(匿名代码块)
匿名代码块 效率远远高于命名代码块
所以在开发中我们尽量选择后者
网友评论