浏览 1803 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-06-07
Ruby的一个很强大的功能就是有把一小片代码作为对象处理的能力。一共有如下的3类: Proc: 一个Proc表现为一个代码块,这个代码块可以带参数调用,并且给出一个返回值。 UnboundMethod: 和Proc比较象。它表现为一个类的实例方法。(要注意的是类方法是一个类对象的实例方法,所以UnboundMethod也可以表现为类方法)。一个UnboundMethod在调用前一定要bound到一个类。 Method: Method 对象是以经通过UnboundMethod#bound 而bound到一个对象的UnboundMethods。还有,他也可以通过Object#method。 我们看一下几种得到Proc和Method对象的方法。下面用Fixnum#+ 作为一个例子。我们一般用双值语法调用他。 3 + 5 # => 8 我们可以用实例方法类调用,如: 3.+(5) # => 8 我们可以用Object#method来得到类的这个实例方法的表现。这个方法会bound到method被调用的对象,3。 add_3 = 3.method(:+) add_3 # => #<Method: Fixnum#+> 这个方法也能转化为Proc,或者直接加参数调用。 add_3.to_proc # => #<Proc:0x00024b08@-:6> add_3.call(5) # => 8 # Method#[] is a handy synonym for Method#call. add_3[5] # => 8 有两种方式可以得到unbound method。一是对对象调用instance_method add_unbound = Fixnum.instance_method(:+) add_unbound # => #<UnboundMethod: Fixnum#+> 我们也可以unbind一个已经被bound到一个对象的方法 add_unbound == 3.method(:+).unbind # => true add_unbound.bind(3).call(5) # => 8 也可以bind这个UnboundMethod到任何一个同一个类的对象 add_unbound.bind(15)[4] # => 19 当然,bind的对象碧血是一个同一个实例,不然会得到一个TypeError的异常 add_unbound.bind(1.5)[4] # => # ~> -:16:in 'bind': bind argument must be an instance of Fixnum (TypeError) # ~> from -:16 得到这个错误的原因是 + 在Fixnum里定义了,这个UnboundMethod对象必须bound到一个kind_of?(Fixnum)的对象。+ 在Numeric定义了(继承Fixnum和Float),上面的代码会得到5.5 block Proc 的转换 当前Ruby执行的一个downside: Block不一定是Proc,反之亦然。普通的block(用do...end或者{}创建的)一定要附在一个方法调用上,并且不是自动对象。比如,不能定义code_block={puts "abc"}。这些是Kernel#lambda和Proc.new 可以做的:把block转化为Proc Kernel#proc 是 Kernel#lambda别名,但是要废弃了。 block_1 = lambda { puts "abc" } # => #<Proc:0x00024914@-:20> block_2 = Proc.new { puts "abc" } # => #<Proc:0x000246a8@-:21> 在Kernel#lambda和Proc.new有一点的不同。用Kernel#lambda产生的Proc是把值返回到调用它的函数;而用Proc.new创建的Proc会使调用它的函数返回,如果失败就会引发一个LocalJumpError的异常,看下面的例子: def block_test lambda_proc = lambda { return 3 } proc_new_proc = Proc.new { return 4 } lambda_proc.call # => 3 proc_new_proc.call # => puts "Never reached" end block_test # => 4 lambda_proc的return语句在lambda里返回了3。相反地,在proc_new_proc的return语句在调用函数block_test里return了,因此block_test返回了4。下面的puts语句不会被执行,因为proc_new_proc.call语句已经在block_test中返回了。 也可以通过把Block传递给一个函数也可以把Block转化成Proc,在函数正常的参数前加&: def some_function(&b) puts "Block is a #{b} and returns #{b.call}" end some_function { 6 + 3 } # >> Block is a #<Proc:0x00025774@-:7> and returns 9 相对的,也可以在函数需要一个block的时候用&替换Proc: add_3 = lambda {|x| x+3} (1..5).map(&add_3) # => [4, 5, 6, 7, 8] 闭包 闭包是在当一个block或者Proc访问在其范围之外的变量的时候创建的。即使包含的代码块出了作用域的范围,这些变量会一直存在当引用他们block或者Proc也出了作用域之后。一个简单的例子,只是说明这个原理: def get_closure data = [1, 2, 3] lambda { data } end block = get_closure block.call # => [1, 2, 3] 在get_closure返回的匿名方法引用了局部的变量数据,这些数据是在匿名方法职位定义的。当这个块变量在作用域,它会保持它自己对这个数据的引用,这个数据的实例会被删除(即使get_closure返回了)。要注意的是每次get_closure被调用的时候,数据引用一个不同的变量(它是一个函数局部的变量): block = get_closure block2 = get_closure block.call.object_id # => 76200 block2.call.object_id # => 76170 函数make_counter是一个典型的闭包的例子。这个函数返回了一个计数函数。每次执行的都会增加并且返回当前的数值。在Ruby里,make_counter是类似的实现: def make_counter(i=0) lambda { i += 1 } end x = make_counter x.call # => 1 x.call # => 2 y = make_counter y.call # => 1 y.call # => 2 这个lambda函数创建了一个闭包,这个壁报包含了当前的局部变量i。不仅仅这个变量是可以访问的,他的值也是可以修改的。每一个闭包得到一个不用的变量的实例(因为这个变量是对应于一个make_counter的实例)。因为x和y包含一个对局部变量i实例的引用,他们有不同的状态。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |