`

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-Functo是ruby中的可组合方法对象

    标题提到的“Ruby-Functo”就是Ruby中实现可组合方法对象的一个库,它为开发者提供了更灵活和模块化的代码组织方式。 Functo库的设计理念是让函数或方法像对象一样可以被操作、组合和复用。在传统的面向对象编程中...

    Ruby-PyCall从Ruby语言调用Python函数

    此外,由于Python的动态特性,可能在运行时才会遇到一些未预见的问题,这需要开发者具备良好的调试技能。 总的来说,Ruby-PyCall是Ruby与Python交互的一个强大工具,它使得混合编程成为可能,让开发者可以充分利用...

    Ruby 面向对象设计实践--2013年

    Ruby作为一种动态类型的面向对象语言,非常适合用来进行面向对象编程的设计和实践。 ##### 1. 类与对象 - **类**:类是对象的模板或蓝图,定义了对象的状态(属性)和行为(方法)。 - **对象**:对象是类的实例,...

    ice最简单实现 ruby调用ice接口 - Ruby - language - ITeye论坛

    标题中的“ice最简单实现 ruby调用ice接口”是指在Ruby编程语言中使用ICE(Internet Communication Engine)框架来实现远程方法调用(RPC)。ICE是由ZeroC开发的一种跨平台、高性能的中间件,它允许不同语言的应用...

    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-Her一个ORM对象关系映射将REST资源映射成Ruby对象

    Ruby是一种动态、面向对象的编程语言,而Her是Ruby社区中的一款ORM(对象关系映射)库,专门用于处理RESTful API。ORM允许开发者用面向对象的方式来操作数据库,而无需直接编写SQL语句,简化了数据操作的过程。Her库...

    Ruby的语法和语言特性总结

    它的面向对象特性、动态类型和鸭子类型让开发者能够快速开发出可维护的代码,而丰富的数据结构如数组和散列表则提供了强大的数据管理能力。Ruby的这些特性使其在Web开发(如Rails框架)和其他领域得到了广泛应用。

    Ruby 语言 Ruby 是一种动态、面向对象的脚本语言,具有简洁而富有表现力的语法 以下是 Ruby 语言的一些特点: 简单

    Ruby 提供了一系列强大的元编程工具,允许开发者在运行时动态地创建和修改类以及对象的行为。这种能力极大地增强了 Ruby 的灵活性,使得开发者能够根据实际需求定制自己的编程框架和库。 #### 五、丰富的内置方法 ...

    Ruby 面向对象知识总结

    #### 一、Ruby的面向对象特性 **Ruby**是一种完全面向对象的编程语言,这意味着在Ruby中,一切事物都是对象。无论是字符串、数字、布尔值甚至是`true`和`false`这样的基本数据类型,都是作为对象处理的。类本身也是...

    Ruby 语言中类与对象简介,入门级语法介绍

    在Ruby中,由于所有东西都是对象,因此可以通过方法调用来实现多态。例如,`+`操作符可以用于整数相加,也可以用于字符串拼接。 #### 二、类的定义 在Ruby中,类是用来定义具有相同属性和行为的对象的模板。类的...

    ruby中文教程,从基础到深入的让你学习ruby

    此外,Ruby的`send`方法可以动态调用方法,`method_missing`可以捕获未定义的方法调用。 Ruby的Gem库是其生态系统的一大亮点,提供大量的第三方库和框架,如Rails(一个流行的Web开发框架)、Sinatra(轻量级Web...

    Ruby元编程pdf

    此外,`Object#send`方法允许动态调用对象的方法,而`instance_variable_set/get`则用于动态设置和获取实例变量。 5. **元类(Meta-class)**:每个Ruby对象都有一个元类,存储了对象的实例方法。元类的概念使得...

    学习 Ruby 的笔记以及 Demo(简介、语言特性、应用场景、Ruby 注释、Ruby 文件的构造与析构语句Ruby 变量等)

    puts '这是 Ruby 文件的运行之前调用' } 2. 文件析构语句 END { puts '这是 Ruby 文件的运行之后调用' } 6、Ruby 变量 变量不能使用保留字,命名规则:小写字母 变量名只能由[a-z_0-1]组成,且只能由字母开头 使用...

    ruby中英文api

    在学习这些文档时,重要的是理解和掌握Ruby的核心理念,即“代码应该是可读的”,这意味着应尽量保持代码简洁,避免冗余,并利用Ruby的动态特性来提高开发效率。此外,熟悉Ruby的Gem生态系统也非常重要,因为Gem是...

    Ruby-一个Ruby的例子

    例如,当你在Ruby中写下"hello",它实际上是一个String对象,你可以调用方法在它上面操作,如`"hello".upcase`会返回"HELLO"。 其次,Ruby的动态性允许你在运行时改变程序的行为。这意味着你可以自由地添加方法到类...

    《Ruby用户指南》中文版

    Ruby的eval函数可以执行字符串作为代码,send方法能够调用对象的任何方法,甚至动态添加或修改方法。这些特性使得Ruby非常适合进行快速开发和原型构建。 Ruby on Rails 是Ruby最著名的Web框架,虽然《Ruby用户指南...

    Ruby元编程 源代码 Metaprogramming Ruby source code

    这本《Metaprogramming Ruby》书籍深入探讨了如何利用Ruby的特性进行元编程,帮助开发者提升代码的灵活性、可扩展性和复用性。源代码提供了书中各个示例的实践,让读者能够更好地理解元编程的概念。 元编程的核心...

    Ruby编程Ruby Programming

    Ruby是一种动态、面向对象的脚本语言,由日本程序员松本行弘(Matz)于1995年设计并发布。它结合了Perl、Smalltalk、Eiffel、Ada以及Lisp等语言的优点,旨在提高开发者的生产力,并且强调代码的可读性和简洁性。 ##...

    ruby脚本交互.rar

    Ruby是一种面向对象的、动态类型的编程语言,以其简洁、优雅的语法著称。在"ruby脚本交互.rar"这个压缩包中,包含的可能是一份使用Ruby语言进行脚本交互的易语言源码。易语言是中国自主研发的一种简单易学的编程语言...

Global site tag (gtag.js) - Google Analytics