`

Ruby动态特性 可调用对象

 
阅读更多
可调用对象:
可调用对象是一个对象,可以给该对象发送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对象也是可调对象,并且,可以解开与它们的实例对象之间的绑定。
分享到:
评论

相关推荐

    ruby教程.rar

    ruby 1.6 特性 ruby 1.7 特性 ruby 1.8 特性 1.6.8到1.8.0的变更点(总结) ruby 1.9 特性 obsolete 对应DOSISH 附录 疑似BNF的Ruby语法 Ruby术语集 Ruby的运行平台 pack模板字符串 sprintf格式 Marshal...

    Ruby菜鸟入门指南.md

    这份文档是Ruby编程入门指南,主要内容包括: 1. 字符串的几种表示方式(单引号、双引号、多行字符串)及其区别。 ...总的来说,这份指南介绍了Ruby作为一种纯面向对象语言的一些核心理念和语法特性。

    starcore_for_winuwp.3.5.0.zip

    •支持分布式功能,CLE将对象的定义和描述同步到客户端,提供远程调用接口,可以在客户端使用对象的属性,调用对象的函数 •提供多种工具,包括:服务家在,打包,发布,调试,管理 •支持telnet客户端,可以直接...

    Ruby的语法和语言特性总结

    Ruby是一种解释型、面向对象、动态类型的语言。Ruby采取的策略是在灵活性和运行时安全之间寻找平衡点。随着Rails框架的出现,Ruby也在2006年前后一鸣惊人,同时也指引人们重新找回编程乐趣。尽管从执行速度上说,...

    Pragmatic - From Java to Ruby jun 2006

    Ruby也许是一个脚本语言改进的榜样,也许又一种面向对象的脚本的Show,也许是大家尝试新技术的一种热情,,但真的让我Fram java to Ruby ,理由太薄弱的,即便的确挺好玩的。 看来,用语言,有时候还看场景。。Java会...

    Ruby开发包 for Script.NET V2.1.1

    Script.NET是蓝蚂蚁工作室开发的一套用于Tcl/Python/Ruby/Lua/NSIS 等脚本语言开发的集成开发环境,刚刚发布的2.1.1版本新增了Ruby脚本的支持,可以支持Ruby脚本的开发、调试、生成可执行文件、控制台等功能,可以...

    候捷谈Java反射机制

    这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。  Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的...

    Lua开发包 for Script.NET V2.1.1

    Script.NET是蓝蚂蚁工作室开发的一套用于Tcl/Python/Ruby/Lua/NSIS 等脚本语言开发的集成开发环境,刚刚发布的2.1.1版本新增了Ruby脚本的支持,可以支持Ruby脚本的开发、调试、生成可执行文件、控制台等功能,可以...

    DynamicBuilder:受 Ruby 的 Builder 启发,用于 C# 4 的令人怀疑的 XML 构造 API

    受到 Ruby 库的极大启发, DynamicBuilder利用新特性,如动态方法调用和可选参数以及匿名对象和委托,以产生一个 API,它完成了不可思议的事情:使用 .NET 进行愉快的XML 构造。 该 API 可以在五分钟内学会并在更短...

    JavaScript权威指南(第6版)(附源码)

    3.7 不可变的原始值和可变的对象引用 3.8 类型转换 3.9 变量声明 3.10 变量作用域 第4章 表达式和运算符 4.1 原始表达式 4.2 对象和数组的初始化表达式 4.3 函数定义表达式 4.4 属性访问表达式 4.5 调用表达式 4.6 ...

    Script.NET脚本语言开发平台2.1.1版本

    Script.NET是蓝蚂蚁工作室开发的一套用于Tcl/Python/Ruby/Lua/NSIS 等脚本语言开发的集成开发环境,刚刚发布的2.1.1版本新增了Ruby脚本的支持,可以支持Ruby脚本的开发、调试、生成可执行文件、控制台等功能,可以...

    JavaScript权威指南(第六版) 清晰-完整

    3.7 不可变的原始值和可变的对象引用 3.8 类型转换 3.9 变量声明 3.10 变量作用域 第4章 表达式和运算符 4.1 原始表达式 4.2 对象和数组的初始化表达式 4.3 函数定义表达式 4.4 属性访问表达式 4.5 调用表达式 4.6 ...

    JavaScript权威指南(第6版)(中文版)

    3.7 不可变的原始值和可变的对象引用 3.8 类型转换 3.9 变量声明 3.10 变量作用域 第4章 表达式和运算符 4.1 原始表达式 4.2 对象和数组的初始化表达式 4.3 函数定义表达式 4.4 属性访问表达式 4.5 调用表达式 4.6 ...

    JavaScript权威指南(第6版)

    3.7 不可变的原始值和可变的对象引用 3.8 类型转换 3.9 变量声明 3.10 变量作用域 第4章 表达式和运算符 4.1 原始表达式 4.2 对象和数组的初始化表达式 4.3 函数定义表达式 4.4 属性访问表达式 4.5 调用表达式 4.6 ...

    Tcl开发包 for Script.NET V2.1.1

    Script.NET是蓝蚂蚁工作室开发的一套用于Tcl/Python/Ruby/Lua/NSIS 等脚本语言开发的集成开发环境,刚刚发布的2.1.1版本新增了Ruby脚本的支持,可以支持Ruby脚本的开发、调试、生成可执行文件、控制台等功能,可以...

    NSIS开发包 for Script.NET V2.1.1

    Script.NET是蓝蚂蚁工作室开发的一套用于Tcl/Python/Ruby/Lua/NSIS 等脚本语言开发的集成开发环境,刚刚发布的2.1.1版本新增了Ruby脚本的支持,可以支持Ruby脚本的开发、调试、生成可执行文件、控制台等功能,可以...

    java开源包1

    Java Remote Desktop 是一个Java 的远程桌面软件,支持很多特性例如文件传输、数据压缩、颜色转换、键盘鼠标事件转换等等。 最短路径算法实现 k-shortest-paths 这是一个实现了 Yen 的排名算法的无环路径的项目 ...

    JavaScript权威指南(第6版)

    3.7 不可变的原始值和可变的对象引用 47 .3.8 类型转换 48 3.9 变量声明 55 3.10 变量作用域 56 第4章 表达式和运算符 60 4.1 原始表达式 60 4.2 对象和数组的初始化表达式 61 4.3 函数定义表达式 62 4.4 属性访问...

    JavaScript权威指南(第6版)中文文字版

    3.7 不可变的原始值和可变的对象引用 47 .3.8 类型转换 48 3.9 变量声明 55 3.10 变量作用域 56 第4章 表达式和运算符 60 4.1 原始表达式 60 4.2 对象和数组的初始化表达式 61 4.3 函数定义表达式 62 4.4 属性访问...

    Script.NET python 开发包

    Script.NET主要功能特性如下: 编辑、调试、运行tcl、python、perl、ruby、lua脚本; 脚本的编译/加密和生成可执行文件(每种脚本语言支持的程度不同); 效率分析等辅助功能(目前只有tcl、perl脚本支持了此功能...

Global site tag (gtag.js) - Google Analytics