[转帖]Ruby动态特性 可调用对象_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3225 | 回复: 0   主题: [转帖]Ruby动态特性 可调用对象        下一篇 
zhongwei.wang
注册用户
等级:中校
经验:1859
发帖:60
精华:1
注册:2013-12-23
状态:离线
发送短消息息给zhongwei.wang 加好友    发送短消息息给zhongwei.wang 发消息
发表于: IP:您无权察看 2013-12-27 9:31:18 | [全部帖] [楼主帖] 楼主

可调用对象:
可调用对象是一个对象,可以给该对象发送call消息,让它执行定义在其中(通常是在一个代码块中)的一些代码。Ruby中主要的可调用对象是 方法、Proc对象和lambda

Proc对象
用一个代码块来实例化Proc类,可以产生一个Proc对象。

pr = Proc.new {puts "Inside a Proc's block"}


这里的代码块是不是会马上执行,只是作为Proc对象的一个定义体保存了下来。如果要执行它,就要发送call消息给这个对象。

pr.call #Inside a Proc's block


作为闭包的Proc对象
首先我们应该清楚:方法定义体中的局部变量和方法调用作用域中的局部变量是两回事,它们毫不相干。

Proc对象的作用域有所不同。Proc.new调用发生时作用域中的局部变量仍然在作用域中。也就是说,不论何时何地调用该对象,那些变量始终是在Proc对象的作用域中。

def call_some_proc(pr)
a = "irrelevant 'a' in method scope"
puts a
pr.call
end
a = "'a' to be used in Proc block"
pr = Proc.new{puts a}
pr.call
call_some_proc(pr)


输出结果:

'a' to be used in Proc block
irrelevant 'a' in method scope
'a' to be used in Proc block


来分析一下这个代码。先看在外围作用域中也有一个变量a,还建立了一个过程对象。接着调用这个过程对象,这时就会输出结果" 'a' to be used in Proc block ",最后是一个方法调用,参数是一个Proc对象pr.现在进入方法call_some_pro中,方法体中有个变理a,再输出a的值,这时就会输出" irrelevant 'a' in method scope ",最后是一个pr.call的调用,由于pr里保存了在外围作用域中的a,所以这里输出的结果仍是外围a的值" 'a' to be used in Proc block "。
像这样带着产生它的上下文信息的一段代码,称为闭包。

Proc对象的参数
Proc创建对象时,代码块中也可以提供接收参数。

>> pr = Proc.new { |x| puts "Call with argument #{x}" }
>> pr.call(100) #Call with argument 100


Proc对象处理它们的参数很微妙,如果给它提供的参数多于定义的个数,那么将得到一个警告。如果没有提供参数,那么该参数变量会被初始为nil,同样也会有一个警告。
如果Proc对象接受多个参数,那么调用它时所提供的参数被赋值给参数列表中的变量。多余的参数被忽略。

也可以使用星号操作符(*)将所有的参数合并为一个参数(数组):

pr = Proc.new{|*x| p x}
pr.call
pr.call(1)
pr.call(1,2)


输出:

[]
[1]
[1, 2]


如果接受多个参数,带星号的参数要放在最后,合并剩余的参数。

pr = Proc.new { |x, *y| p x, y}
pr.call(1, 2, 3)


输出:

1
[2, 3]


用lambda生成匿名函数
lambda用来生成匿名函数,需要做的就是给lambda提供一个代码块,生成的对象其实也是Proc对象,同样可以给它发送call消息。

>> lam = lambda { puts "A lambda" }
>> lam.call
=> A lambda


lambda生成的对象和所有Proc对象一样都是闭包。但是,lambda生成的Proc对象和Proc.new生成的Proc对象之间是差别的。

lambda要比Proc.new严格:

l_proc = lambda {|a, b| puts "a + b = #{a+b}"}
n_proc = lambda {|a, b| puts "a + b = #{a+b}"}
n_proc.call(1, 2, 3)
-> a + b = 3
l_proc.call(1, 2, 3)
-> !ArgumentError (wrong number of arguments (3 for 2))


lambda生成的Proc对象,在使用call调用时,如果参数不对会产生ArgumentError异常。而Proc.new生的Proc对象就不会。

还有一个区别是它们返回值的方式不一样。Proc.new生成的Proc对象是从代码块返回的。

def proc_new
new_proc = Proc.new { return "I got here..." }
new_proc.call
return "...but not here."
end
def lambda_proc
new_proc = lambda { return "You get here..." }
new_proc.call
return "And I got here!"
end
puts proc_new
-> I got here...
puts lambda_proc
-> And I got here!


再论代码块
Ruby中没有Block类或者Block对象,代码块仅在语法意义上存在。代码块是出现在方法调用之后、由大括号(或者do...end)包围起来、等待着被使用的一些代码。
在方法中可以将代码专转换成Proc对象。这可以通过给方法参数加上&来实现,并且这个加上&的变量是在参数的最后一项:

def grab_block(&block)
block.call
end
grab_block { puts "This block will end up in the variable 'block'" }


同样的,使用 & 符号也可以反过来将Proc对象或lambda转换为一个代码块。

lam = lambda { puts "This lambda will serve as a code block" }
grab_block &lam


下面是另一种写法:

grab_block &lambda { puts "This lambda will serve as a code block" }


作为对象的方法
最经常用的是方法,一般使用是间接进行的:给对象发送一条消息,然后对象执行相应的方法。其实方法也是对象,同样可以把它做为对象来处理。
通过method方法,并以方法名作为参数(以字符串或符号的形式),就可以得到方法对象。

class C
def talk
puts "Method-grabbing test! self is #{self}."
end
end
c = C.new
meth = c.method(:talk)


现在meth就是一个方法对象,而是一个绑定的方法对象,这里是绑定在对象c上的。通过给meth发送"call"消息运行。

meth.call


输出结果:

Method-grabbing test! self is #<C:0x35666>.


也可以将方法和它所绑定的对象解绑定(unbind),然后将它绑定(bind)到另一个对象上。只要这个对象和原来的对象��同一个类的实例(或者是子类的实例):

class D < C
end
d = D.new
unbound = meth.unbind
unbound.bind(d).call


输出结果:

Method-grabbing test! self is #<D:0x32d7bc>.


如果不想通过对已绑定的方法使用unbind来得到方法对象,而是直接得到解绑定的方法对象,则可以用instance_method方法,从类来得到。

unbound = C.instance_method(:talk)


这样unbound就是一个解绑定对象,要使用unbound的话,就把它绑定到C的对象上就可以了。

>> c = C.new
>> unbound.bind(c).call


绑定和解绑定的使用相对来说要少很多,能够读懂就够了。在某些场合,对于“怎么做”这个问题的最好答案是,“使用解绑定的方法”。

class A
def a_method
puts "Definition in class A"
end
end
class B < A
def a_method
puts "Definition in class B(subclass of A)"
end
end
class C < B
end


然后获得一个C的实例:

c = C.new


有没有办法叫最底层的实例(这里的c),接收到A类中的消息"a_method"呢?
很显然,默认情况下是不行的,c就会执行它在方法查找路径上第一个可以匹配的方法:

c.a_method


输出结果是:

Definition in class B(subclass of A)


但是可以通过一个解绑定和一个绑定操作解决

class C
def call_original
A.instance_method(:a_method).bind(self).call
end
end


现在直接在c上调用call_original就可以了。
如果想要熟练的掌握Ruby的动态特性,就需要理解该技术,但尽量不要使用。

method对象也是可调对象,并且,可以解开与它们的实例对象之间的绑定。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论