- 浏览: 233312 次
- 性别:
- 来自: 我也来自火星?
文章分类
最新评论
-
chengUFO:
Test tes = c.newInstance();执行以上 ...
自定义ClassLoader -
lliiqiang:
资料太少了,伪造客户端和事先标准以外数据为攻击,其它的是bug ...
Openlaszlo调用JavaRPC和JAVA类通信 -
tianshaojie:
楼主,为什么我安装你的方法建立工程后,访问就出错,我用的是ta ...
Tapestry4入门 -
panshunchang:
发帖过程这么辛苦,还要回答一大堆问题,受不了了
[常用代码整理]JAVA反射 -
活靶子:
生成一个join的SQL语句
SELECT items.* F ...
Better looking URLs with friendly_id
Ruby 动态编程
在介绍ruby动态编程之前,首先看一下,什么叫“动态”语言:
在大部分的编译语言和解释语言中,编写程序和运行程序是两个截然不同的操作,换句话说,编写的代码是确定的,在运行的时候,可能需要对其进行修改。这对于ruby来说非常简单,一个ruby程序,可以在运行的过程中进行修改,甚至可以加入新的代码并且运行,而不需要重新运行该程序。
这种能够修改可执行应用程序数据的能力就叫做元编程。
在ruby代码中,其实我们一直都在进行元编程,虽然可能只是一句非常简单的代码,比如说,在“”中嵌入一个表达式,这就是元编程。毕竟,嵌入的的表达式并非真正的代码,它只是一个字符串,但是ruby却可以将它转换成真正的ruby代码并执行它。
大多数情况下,可以在双引号分隔的字符串中嵌入一些简单的以“#{”和“}”分隔的代码,通常会嵌入一个变量,或是一个表达式:
但是在实际情况中并不能满足于如果简单的表达式,如意愿意的话,应该可以在“”字符串中嵌入任何东西,甚至不需要使用print或puts来显示最终的结果。只要将字符串放置到程序中,ruby就会执行它:
在一个字符串中写出一整个程序可能需要相当的努力。然而,在某些场合,这些功能可能会更有效率。例如,在rails中使用了大量的元编程。事实上,任何程序都将因为可以在程序执行过程中修改程序的行为而受益,这也是元编程最重要的意义。
动态(元编程)特性在RUBY中无处不在。例如,attribute accessors,attr_accessor :aValue就导致了 aValue 和 aValue= 两个方法被创建
eval魔法
在介绍ruby中eval之前,先来看一下大家都比较熟悉的javascript中的eval方法:
在ruby中,eval方法同样地,提供了在字符串中执行ruby表达式的功能。乍看之下,eval方法同在字符串中嵌入#{}的作用一样:
但是,有些时候,结果却并非想象的那样,考虑下面的例子:
假如键入2*4并赋值给变量exp。当使用eval执行exp后结果为8。当使用执行#{ exp }后,结果为“2*4”。这是因为,通过gets()方法接收到的是一个字符串,“#{ }”将它当成字符串处理,而不是一个表达式,但是eval( exp )将它作为一个表达式处理。
为了能够在字符串中执行,需要在字符串中使用eval方法。(尽管可能会使对象执行失败)
下面是另一个示例:
eval方法也可以执行多行字符串,可以用来执行嵌在字符串中的整个程序:
根据eval的特性,看一下下面的程序:
虽然代码并不是太多,但这个小程序可以让你在命令行中创建和执行实际可运行的ruby代码。试着输入下面两个方法(不能输入’q’):
注意你必须在命令行输入全部的方法,程序会分析刚才输入的方法,eval方法转换刚才输入的方法为真实的可执行的ruby代码。可以输入下面的代码去校验一下:
输入结果为:
>>x('hello world')
>>HELLO WORLD
>>y('hello world')
>>dlrow olleh
eval的特殊类型
eval还有几个方法变体:instance_eval, module_eval, class_eval。
instance_eval方法可以通对对象调用,它提供了访问对象实例变量的能力。instance_eval可以通过代码块或是字符串调用:
另外,eval方法不能访问对象中私有的方法(尽管instance_eval方法是公有的)。不过,你可以通过调用public :eval方法明确地改变eval方法的访问属性。但是胡乱地改变基类方法的访问属性并不被推荐。
(严格地说,eval是核心模块的功能并混入到Object类中。)
你可以改变eval方法的访问属性,通过在Object类中添加下面的定义:
当然,当编写独立的代码时,都处于Object类的生命周期内,只需要简单地输入下面的代码(没有Object类的包装),就可以达到同样的效果:
现在可以通过ob实例来调用eval方法:
module_eval,class_eval方法用于模块和类上操作。例如,下面的代码在模块X中添加xyz方法(xyz方法在一个代码块中通过define_method方法定义,并作为X模块的实例方法),在类Y中添加abc方法:
所以,Y的实例就拥有了abc方法,和由X模块混入的xyz方法:
不管它们的名字,module_eval和class_eval在功能上完全相同,并且可以都可以用在模块或者类中:
当然也可以以同样的方式给ruby标准类添加方法:
添加变量和方法
module_eval和class_eval同样可以用来接收变量的值(但是需要记住,这会加强对具体实现类的依赖,破坏了封装性):
事实上,class_eval可以执行随意复杂的表达式。例如可以通过一个字符串来添加一个新的方法:
考虑刚才的在类的外部增加和调用变量的例子(使用class_eval);事实上,在类的内部也提供了同样的方法。这个方法就是 class_variable_get( 该方法接接受一个参数:变量名)和class_variable_set(该方法接受两个参数,第一个参数为变量名,第二个参数为变量的值),下面是使用 这两个方法的示例:
可以通过class_variables方法返回一个包含所有类变量的数组:
当然,也可通过instance_variable_set方法,来为类的实例添加实例变量:
通过组合这些能力,程序员可以在外部完全更改类的结构。例如,可以为类X定义一个addMehtod方法,通过参数m(方法名), 参数&block(方法体)来动态地添加方法:
(send方法会根据第一个参数辨认出相应的方法并调用,并且将其它参数传递给该方法。)
现在,X对象可以调用addMehtod方法给X类增加一个新的方法:
尽管addMethod这个方法是由一个具体的实例调用的(这里是ob这个实例),但它的作用针对的却是这个类,所以该类的其它实例(如ob2),也可以调用由ob实例所添加的新方法:
如果不考虑数据的封装性,可以通过实例的instance_variable_get方法来取得实例变量的值:
同样地,也可以设置和获取常数的值:
既然const_get可以返回一个常量的值,那么就可以通过这个方法来得到一个类的名字,然后使用这个类名,并通过new方法来创建这个类实例。这样就可以在运行时通过提示用户输入相应的类名或方法名来动态地创建类实例以及调用相应的方法:
运行时刻创建类
在上面,我们已经可以修改类的结构,或者创建一个类的实例,但是,我们是否可以在运行时创建一个全新的类?正像const_get可以访问存在的类一 样,const_set方法就可以用来创建一个新的类。下面这个示例将提示用户输入类名,然后创建类,添加一个方法(方法名为myname),创建类的实 例,然后调用刚才添加的方法:
绑定
eval方法有一个可选的参数--binding,如果为指定的话,那么表达式的值就会是一个具体的范围或上下文环境绑定。不过不必为这个有所意外,在 Ruby中,binding方法会返回一个Binding对象的实例,可以使用binding方法返回绑定的值。下是是ruby文档中提供的一个示例:
binding方法是内核的一个私有方法。getBinding方法通过调用binding方法返回当前上下文环境中str的值。在第一次调用eval方 法的时候,当前上下文环境是main对象,并且str的值就是定义的局部变量str的值。在第二次调用eval方法是,当前的上下文环境则是 getBinding方法内部,局部变量str的值现在则为getBinding方法中参数str的值。Binding方法经常作为eval的第二个参数,这样eval就不会因为找不到变量而出错了。
上下文环境也可以在类中定义。在下面的例子中,可以看到,实例变量@mystr和类变量@@x根据类而不同:
SEND
可以使用send方法来调用参数指定的方法:
尽管文档规定send方法必须需要一个方法符号作为参数,但是也可以直接使用一个字符串作为参数,或者,为了保持一致,也可以使用to_sym进行方法名称进行相应的转换后调用:
下面的这个例子显示在运行状态中通过send方法动态地执行指定的方法:
回忆一下上面使用define_method来创建方法的例子,传递了方法的名称m,还为要创建的新方法传递了一个代码块@block
移除方法
除了创建新的方法,有的时候你可能需要移除现有的方法。可以在方法内部使用remove_method方法完成,这将为移除指定的方法:
但是如果子类重写父类的方法,在子类中通过remove_method移除该方法,但父类的方法不会被移除:
相比之下,undef_method方法,就可以避免由于父子类之间存在相同名称的方法而造成最终调用了父类的方法:
处理丢失的方法
当ruby试着去调用一个不存在的方法时( 或者,一个对象发送了一个不能被处理的消息 ),就可能会引起错误并造成程序的终止。你可能更喜欢你编写的程序能够从这样的错误中恢复过来。可以使用method_missing方法,该方法接受一 个方法名,如果该方法不存在,method_missing方法就会被调用:
method_missing也可以处理还有参数的根本就不存在的方法:
method_missing方法甚至可以动态地创建没有定义的方法:
冻结对象
上面讲了许多修改对象的方法,这样对象就可以会在无意中被修改而造成一些不希望结果。事实上,可以使用freeze方法来冻结对象的状态。一旦对象被冻结了,那么任何对此对象的修改,都会引发一个TypeError
异常。注意,一旦对象被冻结,将不能够解冻。
可以使用frozen?方法来检查对象是否被冻结:
frozen方法也可以用来直接冻结一个类(如上面使用的X):
确实应该是instance_variable_get,谢谢指出,已经改正。
绑定 还是讲的比较清楚的,受益良多
为什么不一样呢?ruby版本问题?我1.8.6
你少了 #{ 和 }
为什么不一样呢?ruby版本问题?我1.8.6
这个例子不需要eval吧?我试过结果是:
在介绍ruby动态编程之前,首先看一下,什么叫“动态”语言:
维基百科 写道
动态语言就是一种在运行时可以改变其结构的语言:例如新的函数可以被引进,已有的函数可以被删除等在结构上的变化。众所周知的ECMAScript(JavaScript)便是一个动态语言,除此之外如PHP、Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。
在大部分的编译语言和解释语言中,编写程序和运行程序是两个截然不同的操作,换句话说,编写的代码是确定的,在运行的时候,可能需要对其进行修改。这对于ruby来说非常简单,一个ruby程序,可以在运行的过程中进行修改,甚至可以加入新的代码并且运行,而不需要重新运行该程序。
这种能够修改可执行应用程序数据的能力就叫做元编程。
维基百科 写道
元编程是指某类[计算机程序]的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,或者在[运行时]完成部分本应在[编译时]完成的工作。很多情况下比手工编写全部代码相比工作效率更高。编写元程序的语言称之为元语言,被操作的语言称之为目标语言。一门语言同时也是自身的元语言的能力称之为反射。
在ruby代码中,其实我们一直都在进行元编程,虽然可能只是一句非常简单的代码,比如说,在“”中嵌入一个表达式,这就是元编程。毕竟,嵌入的的表达式并非真正的代码,它只是一个字符串,但是ruby却可以将它转换成真正的ruby代码并执行它。
大多数情况下,可以在双引号分隔的字符串中嵌入一些简单的以“#{”和“}”分隔的代码,通常会嵌入一个变量,或是一个表达式:
aStr = 'hello world' puts( "#{aStr}" ) puts( "#{2*10}" )
但是在实际情况中并不能满足于如果简单的表达式,如意愿意的话,应该可以在“”字符串中嵌入任何东西,甚至不需要使用print或puts来显示最终的结果。只要将字符串放置到程序中,ruby就会执行它:
"#{def x(s) puts(s.upcase) end; (1..3).each{x('hello')}}"
在一个字符串中写出一整个程序可能需要相当的努力。然而,在某些场合,这些功能可能会更有效率。例如,在rails中使用了大量的元编程。事实上,任何程序都将因为可以在程序执行过程中修改程序的行为而受益,这也是元编程最重要的意义。
动态(元编程)特性在RUBY中无处不在。例如,attribute accessors,attr_accessor :aValue就导致了 aValue 和 aValue= 两个方法被创建
eval魔法
在介绍ruby中eval之前,先来看一下大家都比较熟悉的javascript中的eval方法:
w3cschool 写道
The eval() function evaluates a string and executes it as if it was script code.
这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。
这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。
在ruby中,eval方法同样地,提供了在字符串中执行ruby表达式的功能。乍看之下,eval方法同在字符串中嵌入#{}的作用一样:
puts( eval("1 + 2" ) ) puts( "#{1 + 2}" )
但是,有些时候,结果却并非想象的那样,考虑下面的例子:
exp = gets().chomp() puts( eval( exp )) puts( "#{exp}" )
假如键入2*4并赋值给变量exp。当使用eval执行exp后结果为8。当使用执行#{ exp }后,结果为“2*4”。这是因为,通过gets()方法接收到的是一个字符串,“#{ }”将它当成字符串处理,而不是一个表达式,但是eval( exp )将它作为一个表达式处理。
为了能够在字符串中执行,需要在字符串中使用eval方法。(尽管可能会使对象执行失败)
puts( "#{eval(exp)}" )
下面是另一个示例:
print( "Enter the name of a string method (e.g. reverse or upcase): " ) # user enters: upcase methodname = gets().chomp() exp2 = "'Hello world'."<< methodname puts( eval( exp2 ) ) #=> HELLO WORLD puts( "#{exp2}" ) #=> “Hello world”.upcase puts( "#{eval(exp2)}" ) #=> HELLO WORLD
eval方法也可以执行多行字符串,可以用来执行嵌在字符串中的整个程序:
eval( 'def aMethod( x ) return( x * 2 ) end num = 100 puts( "This is the result of the calculation:" ) puts( aMethod( num ))' )
根据eval的特性,看一下下面的程序:
input = "" until input == "q" input = gets().chomp() if input != "q" then eval( input ) end end
虽然代码并不是太多,但这个小程序可以让你在命令行中创建和执行实际可运行的ruby代码。试着输入下面两个方法(不能输入’q’):
def x(aStr); puts(aStr.upcase);end def y(aStr); puts(aStr.reverse);end
注意你必须在命令行输入全部的方法,程序会分析刚才输入的方法,eval方法转换刚才输入的方法为真实的可执行的ruby代码。可以输入下面的代码去校验一下:
x("hello world") y("hello world")
输入结果为:
>>x('hello world')
>>HELLO WORLD
>>y('hello world')
>>dlrow olleh
eval的特殊类型
eval还有几个方法变体:instance_eval, module_eval, class_eval。
instance_eval方法可以通对对象调用,它提供了访问对象实例变量的能力。instance_eval可以通过代码块或是字符串调用:
class MyClass def initialize @aVar = "Hello world" end end ob = MyClass.new p( ob.instance_eval { @aVar } ) #=> "Hello world" p( ob.instance_eval( "@aVar" ) ) #=> "Hello world"
另外,eval方法不能访问对象中私有的方法(尽管instance_eval方法是公有的)。不过,你可以通过调用public :eval方法明确地改变eval方法的访问属性。但是胡乱地改变基类方法的访问属性并不被推荐。
(严格地说,eval是核心模块的功能并混入到Object类中。)
你可以改变eval方法的访问属性,通过在Object类中添加下面的定义:
class Object public :eval end
当然,当编写独立的代码时,都处于Object类的生命周期内,只需要简单地输入下面的代码(没有Object类的包装),就可以达到同样的效果:
public :eval
现在可以通过ob实例来调用eval方法:
p( ob.eval( "@aVar" ) ) #=> "Hello world"
module_eval,class_eval方法用于模块和类上操作。例如,下面的代码在模块X中添加xyz方法(xyz方法在一个代码块中通过define_method方法定义,并作为X模块的实例方法),在类Y中添加abc方法:
module X end class Y @@x = 10 include X end X::module_eval{ define_method(:xyz){ puts("hello" ) } } Y::class_eval{ define_method(:abc){ puts("hello, hello" ) } }
所以,Y的实例就拥有了abc方法,和由X模块混入的xyz方法:
ob = Y.new ob.xyz #=> “hello” ob.abc #=> “hello, hello”
不管它们的名字,module_eval和class_eval在功能上完全相同,并且可以都可以用在模块或者类中:
X::class_eval{ define_method(:xyz2){ puts("hello again" ) } } Y::module_eval{ define_method(:abc2){ puts("hello, hello again" ) } }
当然也可以以同样的方式给ruby标准类添加方法:
String::class_eval{ define_method(:bye){ puts("goodbye" ) } } "Hello".bye #=> “goodbye”
添加变量和方法
module_eval和class_eval同样可以用来接收变量的值(但是需要记住,这会加强对具体实现类的依赖,破坏了封装性):
Y.class_eval( "@@x" ) #=>10
事实上,class_eval可以执行随意复杂的表达式。例如可以通过一个字符串来添加一个新的方法:
ob = X.new X.class_eval( 'def hi;puts("hello");end' ) ob.hi #=> “hello”
考虑刚才的在类的外部增加和调用变量的例子(使用class_eval);事实上,在类的内部也提供了同样的方法。这个方法就是 class_variable_get( 该方法接接受一个参数:变量名)和class_variable_set(该方法接受两个参数,第一个参数为变量名,第二个参数为变量的值),下面是使用 这两个方法的示例:
class X @@aParam = 1000 def self.addvar( aSymbol, aValue ) class_variable_set( aSymbol, aValue ) end def self.getvar( aSymbol ) return class_variable_get( aSymbol ) end end X.addvar( :@@newvar, 2000 ) puts( X.getvar( :@@newvar ) ) #=> 2000 puts( X.getvar( :@@aParam ) )
可以通过class_variables方法返回一个包含所有类变量的数组:
p( X.class_variables ) #=> ["@@abc", "@@newvar"]
当然,也可通过instance_variable_set方法,来为类的实例添加实例变量:
ob = X.new ob.instance_variable_set("@aname", "Bert") p ob.instance_variable_get("@aname") #=> "Bert"
通过组合这些能力,程序员可以在外部完全更改类的结构。例如,可以为类X定义一个addMehtod方法,通过参数m(方法名), 参数&block(方法体)来动态地添加方法:
def addMethod( m, &block ) self.class.send( :define_method, m , &block ) end
(send方法会根据第一个参数辨认出相应的方法并调用,并且将其它参数传递给该方法。)
现在,X对象可以调用addMehtod方法给X类增加一个新的方法:
ob.addMethod( :xyz ) { puts("My name is #{@aname}") }
尽管addMethod这个方法是由一个具体的实例调用的(这里是ob这个实例),但它的作用针对的却是这个类,所以该类的其它实例(如ob2),也可以调用由ob实例所添加的新方法:
ob2 = X.new ob2.instance_variable_set("@aname", "Mary") ob2.xyz #=> My name is Mary
如果不考虑数据的封装性,可以通过实例的instance_variable_get方法来取得实例变量的值:
ob2.instance_variable_get( :@aname )
同样地,也可以设置和获取常数的值:
X::const_set( :NUM, 500 ) puts( X::const_get( :NUM ) )
既然const_get可以返回一个常量的值,那么就可以通过这个方法来得到一个类的名字,然后使用这个类名,并通过new方法来创建这个类实例。这样就可以在运行时通过提示用户输入相应的类名或方法名来动态地创建类实例以及调用相应的方法:
class X def y puts( "ymethod" ) end end print( "Enter a class name: ") #<= Enter: X cname = gets().chomp ob = Object.const_get(cname).new p( ob ) print( "Enter a method to be called: " ) #<= Enter: y mname = gets().chomp ob.method(mname).call
运行时刻创建类
在上面,我们已经可以修改类的结构,或者创建一个类的实例,但是,我们是否可以在运行时创建一个全新的类?正像const_get可以访问存在的类一 样,const_set方法就可以用来创建一个新的类。下面这个示例将提示用户输入类名,然后创建类,添加一个方法(方法名为myname),创建类的实 例,然后调用刚才添加的方法:
puts("What shall we call this class? ") className = gets.strip().capitalize() Object.const_set(className,Class.new) puts("I'll give it a method called 'myname'" ) className = Object.const_get(className) className::module_eval{ define_method(:myname){ puts("The name of my class is '#{self.class}'" ) } } x = className.new x.myname
绑定
eval方法有一个可选的参数--binding,如果为指定的话,那么表达式的值就会是一个具体的范围或上下文环境绑定。不过不必为这个有所意外,在 Ruby中,binding方法会返回一个Binding对象的实例,可以使用binding方法返回绑定的值。下是是ruby文档中提供的一个示例:
def getBinding(str) return binding() end str = "hello" puts( eval( "str + ' Fred'" ) ) #=> "hello Fred" puts( eval( "str + ' Fred'", getBinding("bye") ) ) #=> "bye Fred"
binding方法是内核的一个私有方法。getBinding方法通过调用binding方法返回当前上下文环境中str的值。在第一次调用eval方 法的时候,当前上下文环境是main对象,并且str的值就是定义的局部变量str的值。在第二次调用eval方法是,当前的上下文环境则是 getBinding方法内部,局部变量str的值现在则为getBinding方法中参数str的值。Binding方法经常作为eval的第二个参数,这样eval就不会因为找不到变量而出错了。
上下文环境也可以在类中定义。在下面的例子中,可以看到,实例变量@mystr和类变量@@x根据类而不同:
class MyClass @@x = " x" def initialize(s) @mystr = s end def getBinding return binding() end end class MyOtherClass @@x = " y" def initialize(s) @mystr = s end def getBinding return binding() end end @mystr = self.inspect @@x = " some other value" ob1 = MyClass.new("ob1 string") ob2 = MyClass.new("ob2 string") ob3 = MyOtherClass.new("ob3 string") puts(eval("@mystr << @@x", ob1.getBinding)) #=> ob1 string x puts(eval("@mystr << @@x", ob2.getBinding)) #=> ob2 string x puts(eval("@mystr << @@x", ob3.getBinding)) #=> ob3 string y puts(eval("@mystr << @@x", binding)) #=> main some other value
SEND
可以使用send方法来调用参数指定的方法:
name = "Fred" puts( name.send( :reverse ) ) #=> derF puts( name.send( :upcase ) ) #=> FRED
尽管文档规定send方法必须需要一个方法符号作为参数,但是也可以直接使用一个字符串作为参数,或者,为了保持一致,也可以使用to_sym进行方法名称进行相应的转换后调用:
name = MyString.new( gets() ) # 输入upcase methodname = gets().chomp.to_sym #<= to_sym 并非必需,输入upcase puts name.send(methodname) #=>UPCASE
下面的这个例子显示在运行状态中通过send方法动态地执行指定的方法:
class MyString < String def initialize( aStr ) super aStr end def show puts self end def rev puts self.reverse end end print("Enter your name: ") #<= Enter: Fred name = MyString.new( gets() ) print("Enter a method name: " ) #<= Enter: rev methodname = gets().chomp.to_sym puts( name.send(methodname) ) #=> derF
回忆一下上面使用define_method来创建方法的例子,传递了方法的名称m,还为要创建的新方法传递了一个代码块@block
def addMethod( m, &block ) self.class.send( :define_method, m , &block ) end
移除方法
除了创建新的方法,有的时候你可能需要移除现有的方法。可以在方法内部使用remove_method方法完成,这将为移除指定的方法:
puts( "hello".reverse ) class String remove_method( :reverse ) end puts( "hello".reverse ) #=> „undefined method‟ error!
但是如果子类重写父类的方法,在子类中通过remove_method移除该方法,但父类的方法不会被移除:
class Y def somemethod puts("Y's somemethod") end end class Z < Y def somemethod puts("Z's somemethod") end end zob = Z.new zob.somemethod #=> “Z‟s somemethod” class Z remove_method( :somemethod ) end zob.somemethod #=> “Y‟s somemethod”
相比之下,undef_method方法,就可以避免由于父子类之间存在相同名称的方法而造成最终调用了父类的方法:
zob = Z.new zob.somemethod #=> “Z‟s somemethod” class Z undef_method( :somemethod ) end zob.somemethod #=> „undefined method‟ error
处理丢失的方法
当ruby试着去调用一个不存在的方法时( 或者,一个对象发送了一个不能被处理的消息 ),就可能会引起错误并造成程序的终止。你可能更喜欢你编写的程序能够从这样的错误中恢复过来。可以使用method_missing方法,该方法接受一 个方法名,如果该方法不存在,method_missing方法就会被调用:
def method_missing( methodname ) puts( "#{methodname} does not exist" ) end xxx #=>xxx does not exist
method_missing也可以处理还有参数的根本就不存在的方法:
def method_missing( methodname, *args ) puts( "Class #{self.class} does not understand: #{methodname}( #{args.inspect} )" ) end
method_missing方法甚至可以动态地创建没有定义的方法:
def method_missing( methodname, *args ) self.class.send( :define_method, methodname, lambda{ |*args| puts( args.inspect) } ) end
冻结对象
上面讲了许多修改对象的方法,这样对象就可以会在无意中被修改而造成一些不希望结果。事实上,可以使用freeze方法来冻结对象的状态。一旦对象被冻结了,那么任何对此对象的修改,都会引发一个TypeError
异常。注意,一旦对象被冻结,将不能够解冻。
s = "Hello" s << " world" s.freeze s << " !!!" # Error: "can't modify frozen string (TypeError)"
可以使用frozen?方法来检查对象是否被冻结:
a = [1,2,3] a.freeze if !(a.frozen?) then a << [4,5,6] end
frozen方法也可以用来直接冻结一个类(如上面使用的X):
X.freeze
评论
12 楼
yandongdiy123
2009-10-02
define_method(name) { ... }
定义名为name的实例方法。method可以是Proc、Method或UnboundMethod这三者之一的实例。当指定了method参数时,就返回该参数。若带块调用时,会将块变成Proc化对象,然后返回该对象。
例:
class Foo
def foo() p :foo end
define_method(:bar, instance_method(:foo))
end
Foo.new.bar # => :foo
带块调用时,各版本的处理方式不尽相同。在Ruby 1.7以后的版本中,在执行新定义的方法时,将会在receiver类的实例的基础上对块执行instance_eval。而在Ruby 1.6版本中,只是在块与方法之间取得关联,在执行新定义的方法时,块只是运行在生成时的context中。请参考下例。
class C
end
# 定义实例方法 print_self 。
# 但因为 define_method 是私有方法
# 所以不能直接调用。必须借助于 __send__ 来进行调用。
C.__send__(:define_method, :print_self) { p self }
# 在 1.6 版本中
C.new.print_self #=> main
# 在 1.7 版本中
C.new.print_self #=> #<C:0x4015b490>
定义名为name的实例方法。method可以是Proc、Method或UnboundMethod这三者之一的实例。当指定了method参数时,就返回该参数。若带块调用时,会将块变成Proc化对象,然后返回该对象。
例:
class Foo
def foo() p :foo end
define_method(:bar, instance_method(:foo))
end
Foo.new.bar # => :foo
带块调用时,各版本的处理方式不尽相同。在Ruby 1.7以后的版本中,在执行新定义的方法时,将会在receiver类的实例的基础上对块执行instance_eval。而在Ruby 1.6版本中,只是在块与方法之间取得关联,在执行新定义的方法时,块只是运行在生成时的context中。请参考下例。
class C
end
# 定义实例方法 print_self 。
# 但因为 define_method 是私有方法
# 所以不能直接调用。必须借助于 __send__ 来进行调用。
C.__send__(:define_method, :print_self) { p self }
# 在 1.6 版本中
C.new.print_self #=> main
# 在 1.7 版本中
C.new.print_self #=> #<C:0x4015b490>
11 楼
yandongdiy123
2009-10-02
在类内用define_methods定义一个实例方法,传入闭包内有self,该self为什么不是表示定义它的类上下文?
Person = Class.new
p1 = Person.new
puts p1.class #Person
class Person
define_method :who? do
puts self
end
end
p1.who?
#<Person:0xb8bcdb4>
Person = Class.new
p1 = Person.new
puts p1.class #Person
class Person
define_method :who? do
puts self
end
end
p1.who?
#<Person:0xb8bcdb4>
10 楼
meladet
2009-08-12
好文章,讲得很全很系统
9 楼
rainlife
2009-05-07
wolfplanet 写道
我可是全都看完了,发现了个小错
下面的那个应该是instance_variable_get
引用
ob = X.new ob.instance_variable_set("@aname", "Bert") p ob.instance_variable_set("@aname") #=> "Bert"
下面的那个应该是instance_variable_get
确实应该是instance_variable_get,谢谢指出,已经改正。
8 楼
wolfplanet
2009-05-06
我可是全都看完了,发现了个小错
下面的那个应该是instance_variable_get
引用
ob = X.new ob.instance_variable_set("@aname", "Bert") p ob.instance_variable_set("@aname") #=> "Bert"
下面的那个应该是instance_variable_get
7 楼
weskycn
2009-05-04
aone 写道
看完了.但绑定那块没太明白.
绑定 还是讲的比较清楚的,受益良多
6 楼
RednaxelaFX
2009-04-28
好文~
Ruby的string interpolation,字符串插值应该还算不上元编程吧。至少Ruby的字符串插值并不带来任何“程序操纵或改变程序”的能力。
在调用Ruby的eval时,作为参数传给eval的字符串就是普通的字符串,在Ruby解析源码的时候不会做任何特殊的处理。而使用#{}形式(及其简写形式)的字符串插值时,Ruby解析器会为插值代码生成对应的AST,保存在一个NODE_EVSTR里。
可以简单的验证它们行为的差别:
可以观察到前者在还没有输出start就报语法错了,而后者是输出start之后才报语法错。这是解析时机的差异造成的,也显示了Ruby的字符串插值中的代码并不“只是一个字符串”。
rainlife 写道
在ruby代码中,其实我们一直都在进行元编程,虽然可能只是一句非常简单的代码,比如说,在“”中嵌入一个表达式,这就是元编程。毕竟,嵌入的的表达式并非真正的代码,它只是一个字符串,但是ruby却可以将它转换成真正的ruby代码并执行它。
大多数情况下,可以在双引号分隔的字符串中嵌入一些简单的以“#{”和“}”分隔的代码,通常会嵌入一个变量,或是一个表达式
大多数情况下,可以在双引号分隔的字符串中嵌入一些简单的以“#{”和“}”分隔的代码,通常会嵌入一个变量,或是一个表达式
Ruby的string interpolation,字符串插值应该还算不上元编程吧。至少Ruby的字符串插值并不带来任何“程序操纵或改变程序”的能力。
在调用Ruby的eval时,作为参数传给eval的字符串就是普通的字符串,在Ruby解析源码的时候不会做任何特殊的处理。而使用#{}形式(及其简写形式)的字符串插值时,Ruby解析器会为插值代码生成对应的AST,保存在一个NODE_EVSTR里。
可以简单的验证它们行为的差别:
puts 'start' puts "#{1 +* 2}" # 这段插值代码有语法错误 puts 'end'
puts 'start' puts eval('1 +* 2') # 这段传给eval的参数中代码有语法错误 puts 'end'
可以观察到前者在还没有输出start就报语法错了,而后者是输出start之后才报语法错。这是解析时机的差异造成的,也显示了Ruby的字符串插值中的代码并不“只是一个字符串”。
5 楼
night_stalker
2009-04-28
aone 写道
为什么不一样呢?ruby版本问题?我1.8.6
你少了 #{ 和 }
4 楼
aone
2009-04-28
C:\Documents and Settings\w>irb irb(main):001:0> "def x(s);puts(s.upcase);end;(1..3).each{x('hello')}" => "def x(s);puts(s.upcase);end;(1..3).each{x('hello')}" irb(main):002:0> "def x(s) irb(main):003:0" puts(s.upcase) irb(main):004:0" end; irb(main):005:0" (1..3).each{x('hello')}" => "def x(s)\nputs(s.upcase)\nend;\n(1..3).each{x('hello')}" irb(main):006:0> irb(main):007:0* irb(main):008:0* eval("def x(s);puts(s.upcase);end;(1..3).each{x('hello')}") HELLO HELLO HELLO => 1..3 irb(main):009:0>
为什么不一样呢?ruby版本问题?我1.8.6
3 楼
Hooopo
2009-04-28
aone 写道
"def x(s)
puts(s.upcase)
end;
(1..3).each { x('hello') }"
会吗?我试了要eval才行.
puts(s.upcase)
end;
(1..3).each { x('hello') }"
引用
只要将字符串放置到程序中,ruby就会执行它
会吗?我试了要eval才行.
这个例子不需要eval吧?我试过结果是:
C:\Documents and Settings\xp2008>irb irb(main):001:0> "#{def x(s) irb(main):002:0" puts(s.upcase) irb(main):003:0" end; irb(main):004:0" (1..3).each{x('hello')}}" HELLO HELLO HELLO => "1..3"
2 楼
aone
2009-04-28
看完了.但绑定那块没太明白.
1 楼
aone
2009-04-28
"def x(s)
puts(s.upcase)
end;
(1..3).each { x('hello') }"
会吗?我试了要eval才行.
puts(s.upcase)
end;
(1..3).each { x('hello') }"
引用
只要将字符串放置到程序中,ruby就会执行它
会吗?我试了要eval才行.
发表评论
-
[翻译]如何学习ruby和rails
2009-08-06 12:36 1991已经有人全部翻译:http://qichunren.iteye ... -
在netbeans6.7中使用rspec1.2.7
2009-07-10 14:20 1190将开发工具换成了netbeans6.7,rspec的版本,也变 ... -
MySQL5.1.x的驱动有问题?
2009-03-29 18:36 1215今天把MySQL换成了5.1版本的,使用rake db:cre ... -
ActiveRecord级联删除
2009-03-30 21:00 2667Rails在关联关系中,han_o ... -
install ruby on rails, sqlite3, sqlite3-ruby under ubuntu8.10
2009-04-01 14:57 1322nothing but the script I used: ... -
Rails Plugin: Easy Fckeditor
2009-04-02 22:57 1373#安装easy fckeditor插件,需要首先安装git ... -
修改Easy_Fckeditor上传图片的目录
2009-04-03 09:32 1144修改文件上传目录 easy_fckeditor默认的文件上传目 ... -
[转]Why I like Ruby #1: alias_method
2009-04-03 16:18 1048So you found yourself in the ne ... -
Rails中的namespace
2009-04-10 16:56 1316在Rails中可以通过namespace来管理controll ... -
Rails中namespace的layout
2009-04-15 10:57 1781在rails中提供了namespace的功能,但是如何实现na ... -
也用上了windows 7,不过gem用不起来
2009-05-03 17:57 1899今天早上将系统换成了windows7了,在试用下来,从发到上来 ... -
Rails生成Ext Tree
2008-03-29 01:45 2873在Rails中使用has_one 、has_many 、bel ... -
Rails表单
2008-03-17 22:47 1466User为和Address类是一个一对多的关系: User.r ... -
ruby操作word2
2008-03-15 18:59 21require 'win32ole' msword = WI ... -
ruby操作word
2008-03-15 18:56 23require 'win32ole' word = WIN3 ... -
ruby操作WORD文档生成HTML
2008-03-15 18:54 2680通过ruby代码,将指定的WORD文档转换为HTML: r ... -
Netbeans,无法承受之慢
2007-11-26 20:29 3615Netbenas,现在已经是Beta2了,应该说,在功能上,已 ...
相关推荐
- 《Ruby编程语言》是一本权威的Ruby学习指南。 - **在线教程** - 如RubyMonk、Codecademy等网站提供交互式的Ruby学习课程。 通过上述介绍,我们可以看到Ruby不仅具备简洁优雅的语言特性,还有着丰富的生态系统和...
Ruby是一种跨平台、面向对象的动态类型编程语言。Ruby 体现了表达的一致性和简单性,它不仅是一门编程语言,更是表达想法的一种简练方式。 Ruby 是一个注重均衡的语言,它的发明者松本行弘(Yukihiro “Matz” ...
Ruby元编程是编程领域中一个深入且强大的主题,它允许程序员在运行时修改或创建代码,极大地提高了灵活性和代码的动态性。这本书“Ruby元编程第二版”专注于讲解Ruby语言的这一独特特性,旨在帮助开发者更好地理解和...
元编程技术广泛应用于多种编程语言中,其中Ruby因其灵活的语言特性和动态特性而成为元编程的理想选择之一。 #### 1.2 Ruby中的元编程特点 - **动态性**:Ruby是一种动态类型语言,这意味着可以在运行时修改类和对象...
ruby元编程.pdf ruby 元编程 这本书对ruby的调用原理做了非常精辟的分析。 ruby 元编程 这本书对ruby的元编程方式做了比较好的阐释。 ruby 元编程 这本书对结对编程有一个很好的提现。
Ruby元编程是编程的一种高级技巧,它允许程序员在运行时动态地修改或创建代码,极大地提高了灵活性和代码的可扩展性。Ruby作为一种动态类型语言,其元编程能力尤为强大,使得开发者可以创建出高度定制化的解决方案。...
通过分析案例、讲解例题、回顾Ruby代码库的实现细节,作者不仅向读者展示了Ruby编程的优势和Ruby特有的解决问题的方式,更详细开列出发挥其优势的技巧和常用的Ruby设计模式。Ruby之父松本行弘作 序推荐。
Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门与实践 Ruby编程语言入门...
Ruby元编程中文版
本资料包主要关注的是基于Ruby编程的基础知识和应用,包括如何使用Ruby进行开发和解决问题。 Ruby的设计哲学强调程序员的生产力和代码的可读性。它的语法直观,使得初学者能够快速上手。Ruby的核心特性包括: 1. ...
元编程的核心在于动态性,Ruby作为一种动态类型语言,其元编程能力非常强大。例如,你可以动态创建方法、修改类或模块的结构、以及在运行时检查对象的属性和行为。这些功能主要通过Ruby的一些核心机制实现,包括: ...
### Ruby编程语言简介 #### 一、Ruby编程语言概述 Ruby是一种动态的、面向对象的、通用型的编程语言,自1995年由日本程序员松本行弘(Yukihiro Matsumoto)创建以来,便以其简洁优雅的语法、高度可读性和易于学习...
ruby元编程 第2版 中文 目录清晰 方便广大通勤族路上看,如果喜欢请购买正版纸质图书
在Ruby这种动态编程语言中,元编程是一种核心技术,它不仅仅是一种技巧,更是Ruby和Rails框架中用于提高开发效率和软件复用性的关键方法。 在Ruby语言中,元编程主要通过方法拦截、动态方法定义、元类以及一些特殊...
1. **对象模型与类的动态行为**:Ruby的对象模型非常灵活,本书详细解释了如何利用这一特点进行元编程。例如,可以通过定义方法的缺省行为来简化代码,或者通过动态添加方法来扩展类的功能。 2. **闭包与块**:闭包...
如何设计一套Ruby编程技术课程体系+编程知识+技术开发; 如何设计一套Ruby编程技术课程体系+编程知识+技术开发; 如何设计一套Ruby编程技术课程体系+编程知识+技术开发; 如何设计一套Ruby编程技术课程体系+编程知识...
- **Kosmas Chatzimichalis**(软件工程师):本书不仅深入讲解了Ruby及其框架Ruby on Rails的核心机制,还提供了一系列宝贵的工具和技巧,帮助读者将Ruby编程水平提升到一个新的高度。 - **Arialdo Martini**...