【问题标题】:Ruby - Using class_eval to define methodsRuby - 使用 class_eval 定义方法
【发布时间】:2012-03-22 14:17:55
【问题描述】:

我正在上斯坦福大学的 SaaS 课程,尝试完成this assignment 的第 5 部分

我很难理解这个概念,这就是我试图做的:

class Class
  def attr_accessor_with_history(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name
    attr_reader attr_name + '_history'
    class_eval %Q'{def #{attr_name}(a);#{attr_name}_history.push(a) ; end;}'
  end
end

我可能做错了各种各样的事情,阅读了关于元编程的 The Book Of Ruby 章节,但我仍然不明白,有人可以帮我理解这一点吗?

【问题讨论】:

  • 这真的有效吗?如果不是,有什么问题?不太确定这里的问题是什么!
  • 看看stackoverflow.com/questions/9658724/…是同一个作业题
  • 是的,到我第一次问的日期为止:P

标签: ruby metaprogramming class-eval


【解决方案1】:

你必须注意到#{attr_name}_history是一个实例变量,所以在前面使用@,就像下面类中的@foo

def #{attr_name}=value,#{attr_name}=是方法名,value是参数,同def func parameter

def #{attr_name}=value
  (!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
end

【讨论】:

    【解决方案2】:

    关于您所做的事情,您实际上正处于解决方案的风口浪尖。只是您的代码中不存在#{attr_name}_history。如果不存在,您将需要创建一个实例变量并将其设置为 nil。如果确实存在,您应该处理推入数组的内容。

    有几种方法可以做到这一点。一种方法是if defined? @#{attr_name}_history DoStuffHere

    【讨论】:

      【解决方案3】:

      这很有趣!!!

      class Class
          def attr_accessor_with_history(attr_name)
              attr_name = attr_name.to_s # make sure it's a string
              attr_reader attr_name
              attr_reader attr_name+"_history"
              class_eval %Q"
                  def #{attr_name}=(value)
                      if !defined? @#{attr_name}_history
                          @#{attr_name}_history = [@#{attr_name}]
                      end
                      @#{attr_name} = value
                      @#{attr_name}_history << value
                  end
              "
          end
      end
      
      class Foo
          attr_accessor_with_history :bar
      end
      
      class Foo2
          attr_accessor_with_history :bar
          def initialize()
              @bar = 'init'
          end
      end
      
      f = Foo.new
      f.bar = 1
      f.bar = nil
      f.bar = '2'
      f.bar = [1,nil,'2',:three]
      f.bar = :three
      puts "First bar:", f.bar.inspect, f.bar_history.inspect
      puts "Correct?", f.bar_history == [f.class.new.bar, 1, nil, '2', [1,nil,'2',:three], :three] ? "yes" : "no"
      old_bar_history = f.bar_history.inspect
      
      f2 = Foo2.new
      f2.bar = 'baz'
      f2.bar = f2
      puts "\nSecond bar:", f2.bar.inspect, f2.bar_history.inspect
      puts "Correct?", f2.bar_history == [f2.class.new.bar, 'baz', f2] ? "yes" : "no"
      
      puts "\nIs the old f.bar intact?", f.bar_history.inspect == old_bar_history ? "yes" : "no"
      

      请注意,您需要在 class_eval 中使用字符串的唯一原因是您可以在定义自定义 setter 时引用 attr_name 的 值。否则通常会将一个块传递给class_eval。

      【讨论】:

      • 解释概念!不要只给出答案!你真的伤害的人多于帮助的人。
      • 这个部分是做什么的? def #{attr_name}=(值)。我的意思是我们在这里实际定义的是什么?
      • 除非您有重要的贡献,否则请不要重提多年前的问题,谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-11
      • 1970-01-01
      • 1970-01-01
      • 2012-10-21
      • 1970-01-01
      相关资源
      最近更新 更多