利用ruby演示程序执行

介绍

Ruby支持元编程,简单的说就是在运行时改变程序自身。利用这一点,可以做出很有趣的程序。本文介绍利用ruby演示程序的执行过程,显示包括函数调用和数据变化。

演示程序执行

Ruby每个class都有一个方法method_missing(name, *args)。当调用对象的方法不存在时,系统默认调用这个方法来处理。这就相当于一个系统后门,可以得到方法调用的轨迹。

下面的程序演示如何把大象放进冰箱里。

class User

  def act

    f = Frig.new

    f.open

    f.put_elephant

    f.close

  end

end

class Frig

  def indent; ' '*4; end

  def method_missing(name, *args)

    if name[0]=='_'

      return

    end

    puts "#{indent}Frig.#{name} "

  end

end

user = User.new

user.act

输出为:

    Frig.open

    Frig.put_elephant

    Frig.close

这里面user.act没有打印出来。为了做到这一点,需要把method_missing放到一个公共类中共享;而且需要把act变成未知方法,激活method_missing;然后再利用一个技巧,映射到已知的方法。

在这里把已知方法定义成下划线开头,在调用的地方取掉下划线,让method_missing完成两者之间的连接。

class Show

  def initialize(name = "", pos = 0)

    @name, @pos = name, pos

  end

 

  def indent; ' '*@pos*4; end

 

  def method_missing(name, *args)

    if name[0]=='_'

      return

    end

    puts "#{indent}#{@name}.#{name} "

    if methods.grep(/_#{name}/)

      send "_#{name}", *args

    end

  end

end

 

class User < Show

  def _act

    f = Frig.new("frig", 1)

    f.open

    f.put_elephant

    f.close

  end

end

 

class Frig < Show

end

 

user = User.new

user.act

输出为:

user.act

    frig.open

    frig.put_elephant

    frig.close

添加数据显示

上一节演示了程序的执行,但是充其量只是一个调用树。为了进一步揭示程序的运行状态,这一节加入状态机。

class Stm

  def initialize

    @edges = Hash.new

    @current = '$'

  end

 

  def get_current; @current; end

  def empty?; @edges.empty?; end

 

  def def_edge(from, to, event)

    edges = @edges[from.to_s]

    if edges==nil

      @edges[from.to_s] = Hash.new

    end

    @edges[from.to_s][event.to_s] = to.to_s

  end

 

  def accept(event)

    edges = @edges[@current.to_s]

    st = nil

    if edges!=nil

      st = edges[event.to_s]

      if st!=nil

        @current = st

      end

    end

    if st==nil

      "**error** #{event} is not accepted "

    else

      st

    end

  end

  end

 

class Show

  def initialize(name = "", pos = 0)

    @name, @pos = name, pos

    @m_stm = Stm.new

  end

 

  def indent; ' '*@pos*4; end

 

  def method_missing(name, *args)

    if name[0]=='_'

      return

    end

    print "#{indent}#{@name}.#{name} "

    if !@m_stm.empty?

      error = @m_stm.accept(name.to_s)

      if error && error[0]=='*'

        puts error+" when #{@name}'s  = #{@m_stm.get_current}"

      else

        puts 'STATUS("'+error+"\")"

      end

    else

      puts ""

    end

    if methods.grep(/_#{name}/)

      send "_#{name}", *args

    end

  end

end

 

class User < Show

  def _act

    f = Frig.new("frig", 1)

    f.open

    f.put_elephant

    #f.put_elephant

    f.close

  end

end

 

class Frig < Show

  def initialize(name = "", pos = 0)

    super

    @m_stm.def_edge('$', 'door is opened', 'open')

    @m_stm.def_edge('door is opened', 'door is closed', 'close')

    @m_stm.def_edge('door is opened', 'door is opened w/ elephant', 'put_elephant')

    @m_stm.def_edge('door is opened w/ elephant', 'door is closed w/ elephant', 'close')

  end

end

 

输出:

user.act

    frig.open STATUS("door is opened")

    frig.put_elephant STATUS("door is opened w/ elephant")

frig.close STATUS("door is closed w/ elephant")

如果把大象两次放入冰箱,程序还会报错:

user.act

    frig.open STATUS("door is opened")

    frig.put_elephant STATUS("door is opened w/ elephant")

    frig.put_elephant **error** put_elephant is not accepted  when frig's  = door is opened w/ elephant

    frig.close STATUS("door is closed w/ elephant

posted @ 2012-11-11 14:44  yunfeng_net  阅读(595)  评论(0)    收藏  举报