分享 元编程 基础方法整理

skpark1987 · May 21, 2015 · Last by hengzou replied at July 02, 2015 · 7152 hits
Topic has been selected as the excellent topic by the admin.

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

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

比较全面,赞作者

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

元编程大法好

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

:plus1: ,不过有点小错误

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

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

关于 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")

#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

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

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

说个小问题 上面的代码

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..."
=> ""

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

学习了

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 MyString < String
  def initialize( aStr )
    super aStr  #这里调用super的时候aStr参数可以去掉super调用是会带上参数的如不想带参数可用super()
  end
  def show
    puts self
  end
  def rev
    puts self.reverse
  end
end
You need to Sign in before reply, if you don't have an account, please Sign up first.