分享 元编程 基础方法整理

skpark1987 · 发布于 2015年05月21日 · 最后由 hengzou 回复于 2015年07月02日 · 3857 次阅读
17502
本帖已被设为精华帖!

eval

eval是Object中的private方法。但是你可以通过发送名称到public方法的方式更改它的visibility。

class Object
  public :eval
end
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可以evaluate多行字符串,使能够执行字符串中的整个程序。

eval( 'def aMethod( x )
    return( x * 2 )
  end
  num = 100
  puts( "这是计算后的结果:" )
  puts( aMethod( num ))' )

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"
p( ob.eval( "@aVar" ) ) # 错误,你需要更改eval方法为public

class_eval(module_eval)

功能跟instance_eval类似,但是它是执行在class或module中,它们俩可以互换着使用。

module X
end
class Y
  @@x = 10
  include X
end
X::module_eval{ define_method(:xyz){ puts("hello" ) } }  # 在模块中定义xyz方法
Y::class_eval{ define_method(:abc){ puts("hello, hello" ) } } # 在类中定义abc方法
y = Y.new # 现在Y有abc和xyz方法

操作变量

类变量

class_variable_set( aSymbol, aValue )
class_variable_get( aSymbol )
class_variables # return class variable array

实例变量

instance_variable_set( aSymbol, aValue )
instance_variable_get( aSymbol )

常量

const_set( aSymbol, aValue )
const_get( aSymbol )
constants

动态创建实例

class X
  def y
    puts( "ymethod" )
  end
end
print( "请输入类名: ") #输入: X
cname = gets().chomp
ob = Object.const_get(cname).new #执行 X.new 方法
p( ob )
print( "请输入要执行的方法: " ) #<= 输入: y
mname = gets().chomp
ob.method(mname).call

动态创建类

puts("请输入咬创建的类的名称? ")
className = gets.strip().capitalize()
Object.const_set(className,Class.new) #使用set方法,创建类
puts("我会给它创建一个叫myname的方法" )
className = Object.const_get(className)
className::module_eval{ define_method(:myname){
  puts("我的类的名称为 '#{self.class}'" ) }
}
x = className.new
x.myname

BINDINGS

eval方法有一个可选的 binding参数,如果提供的话,将会在指定的上下文执行evaluation。类似于JS中的apply,call方法的第一个参数。

def getBinding(str)
  return binding()
end
str = "hello"
puts( eval( "str + ' Fred'" ) ) #没指定Binding,因此str为当前环境下的str变量。=> "hello Fred"
puts( eval( "str + ' Fred'", getBinding("bye") ) ) #指定了Binding,因此在getBinding上下文中寻找str变量。=> "bye Fred"

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

send

可以通过使用send方法,调用指定:symbol名称的方法。

class MyString < String
  def initialize( aStr )
    super aStr
  end
  def show
    puts self
  end
  def rev
    puts self.reverse
  end
end
print("输入你的名字: ") #<= 输入: Fred
name = MyString.new( gets() )
print("输入方法名: " ) #<= 输入: rev
methodname = gets().chomp.to_sym
puts( name.send(methodname) ) #=> derF

##动态创建/删除方法

#创建方法
def addMethod( m, &block )
  self.class.send( :define_method, m , &block )
end
#删除方法
class String
  remove_method( :reverse )
end

如果同样的名称的方法在祖先类中已定义,则祖先类中的方法不会被删除。

class Y
  def somemethod
    puts("Y的方法")
  end
end
class Z < Y
  def somemethod
    puts("Z的方法")
  end
end
zob = Z.new
zob.somemethod #=> “Z的方法”
class Z
  remove_method( :somemethod )
end
zob.somemethod #=> “Y的方法”

undef_method

防止调用某方法,不管该方法是否已被定义。

zob = Z.new
zob.somemethod #=> “Z的方法”
class Z
  undef_method( :somemethod )
end
zob.somemethod #=> "undefined method‟ error

method_missing

捕捉不存在的方法

def method_missing( methodname )
 puts( "Sorry, #{methodname} does not exist" )
end
xxx #=> Sorry, xxx does not exist
共收到 16 条回复
15816

代码就是字符串,就是把字符串拼成合法的代码,这就是ruby的元编程,抽象能力弱。

12381

比较全面,赞作者

96

推荐读一下Metaprogramming Ruby的附录,能够掌握很多实用的技巧

594

元编程大法好

11955

#3楼 @ftiasch 同感,我是偶尔看到发现的 😏

8596

:plus1: ,不过有点小错误

constants_set( aSymbol, aValue )  # 应为const_set
constants_get( aSymbol )          # 应为const_get
17502

#6楼 @huopo125 感谢指正,已修改。

1181

关于 BINDINGS,楼主的最后3行输出结果应该分别是

puts(eval("@mystr << @@x", ob1.getBinding)) #=> ob1 string some other value
puts(eval("@mystr << @@x", ob2.getBinding)) #=> ob2 string some other value
puts(eval("@mystr << @@x", ob3.getBinding)) #=> ob3 string some other value

因为当 @@x = " some other value" 在顶层环境执行完后,相当于执行了 Object.class_variable_set("@@x", "some other value"),而MyClass、MyOtherClass和Object都处于同一个Class Tree中,它们都会共享父类的类变量,此时 MyClass.class_variable_get("@@x")MyOtherClass.class_variable_get("@@x") 都相当于是 Object.class_variable_get("@@x")

8744

#8楼 @lifuzho 好像

@@x = " some other value"

这句把 @@x “拉” 到了两个 class 的外面, 这句之后 @@x 就属于 toplevel class variable了。变成所有类共享了。

把他移到最上面可以发现输出变成 y

puts(eval("@mystr << @@x", ob1.getBinding)) #=> ob1 string y
puts(eval("@mystr << @@x", ob2.getBinding)) #=> ob2 string y
puts(eval("@mystr << @@x", ob3.getBinding)) #=> ob3 string y

解释器还会给出警告:

xxxxxx.rb:xx: warning: class variable access from toplevel

17502

#8楼 @lifuzho 恩,写错了,应该注释掉top level中的赋值。

17004

好赞先mark。这几天一直在找meta programming的东西

10912

说个小问题 上面的代码

class MyString < String
  def initialize( aStr )
    super aStr  #这里调用super的时候,aStr参数可以去掉,super调用是会带上参数的,如不想带参数可用super()
  end
  def show
    puts self
  end
  def rev
    puts self.reverse
  end
end

可以看下这个例子

class Animal
  def say *arg
    arg.each{|a| puts a}
  end
end

class Dog < Animal
  def say *arg
    super #调用super会把参数带过去
  end   
end

class Cat < Animal
  def say *arg
    super() #没有把参数带过去
  end
end

d = Dog.new
d.say "wang wang..."
=> "wang wang..."

c = Cat.new
c.say "miao miao..."
=> ""
16793

谢谢分享,这元编程得好好学习一下!

19265

学习了

96

class MyString < String def initialize( aStr ) super aStr  #这里调用super的时候,aStr参数可以去掉,super调用是会带上参数的,如不想带参数可用super() end def show puts self end def rev puts self.reverse end end

96
class MyString < String
  def initialize( aStr )
    super aStr  #这里调用super的时候,aStr参数可以去掉,super调用是会带上参数的,如不想带参数可用super()
  end
  def show
    puts self
  end
  def rev
    puts self.reverse
  end
end
23196 Catherine 怎么通过变量 去动态的取得 变量名? 中提及了此贴 03月24日 16:04
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册