【问题标题】:How do I call a method that is a hash value?如何调用作为哈希值的方法?
【发布时间】:2010-11-07 11:32:02
【问题描述】:

之前,我询问了一种在给定条件“Ruby a clever way to execute a function on a condition”下执行方法的巧妙方法。

解决方案和响应时间都很棒,但是,在实施时,拥有一个 lambdas 散列很快就会变得丑陋。于是我开始尝试。

以下代码有效:

def a()
  puts "hello world"
end

some_hash = { 0 => a() }

some_hash[0]

但如果我将它包装在一个类中,它就会停止工作:

class A

  @a = { 0 => a()}

  def a()
    puts "hello world"
  end


  def b()
    @a[0]
  end

end

d = A.new()

d.b()

我不明白为什么它应该停止工作,谁能建议如何让它工作?

【问题讨论】:

    标签: ruby hash methods call


    【解决方案1】:

    该代码不起作用。它在添加到哈希时执行a,而不是在从哈希中检索到时执行(在 irb 中尝试)。

    它在类中不起作用,因为在类上没有定义 a 方法(您最终在实例上定义了一个方法 a

    尝试实际使用像

    这样的 lambdas
    {0 => lambda { puts "hello world" }} 
    

    改为

    【讨论】:

    • 这确实是我试图避免的。我放在 lambda 中的代码可能会有点混乱,我将在哈希中包含大约 15 个元素。它使代码有点混乱。
    • @Peter:lambda 中的代码是否比方法中的代码更混乱?
    • 然后将方法名称作为符号传递并使用 send 或 Symbol#to_proc。
    • @Chuck 它是相同的,但是将代码包装在方法中然后在哈希中引用它们似乎是一个更漂亮的解决方案。
    • 您是否考虑过创建一个类方法,该方法可以接受一个块并将其作为 lambda 表达式添加到您的哈希中,然后在您的类主体中根据需要使用它。
    【解决方案2】:

    首先,您没有将 lambda 放入哈希中。您将调用 a() 的结果放在当前上下文中。

    鉴于此信息,请考虑您班级中的代码的含义。类定义的上下文是类。因此,您定义了一个名为a 的实例方法,并将一个类实例变量分配给包含在当前上下文中调用a 的结果的散列。当前上下文是类 A,而类 A 没有名为 a 的类方法,因此您试图将不存在的方法的结果放在那里。然后在实例方法b 中,您尝试访问一个名为@a 的实例变量——但没有。类上下文中定义的@a 属于类本身,而不是任何特定的实例。

    所以首先,如果你想要一个 lambda,你需要创建一个 lambda。其次,您需要清楚类和该类的实例之间的区别。

    如果你想列出在特定条件下被调用的方法名,你可以这样做:

    class A
      def self.conditions() { 0 => :a } end
    
      def a
        puts "Hello!"
      end
    
      def b(arg)
        send self.class.conditions[arg]
      end
    end
    

    这将条件散列定义为类的方法(使其易于访问),散列仅包含要调用的方法的名称,而不是 lambda 或类似的东西。所以当你调用b(0)时,它sends本身就是A.conditions[0]中包含的消息,即a

    【讨论】:

      【解决方案3】:

      如果你真的只是想美化这种东西, 为什么不将所有方法都包装在一个类中:

      # a container to store all your methods you want to use a hash to access
      class MethodHash
        alias [] send
        def one
          puts "I'm one"
        end
        def two
          puts "I'm two"
        end
      end
      
      x = MethodHash.new
      x[:one] # prints "I'm one"
      x.two # prints "I'm one"
      

      或者,使用您的示例:

      # a general purpose object that transforms a hash into calls on methods of some given object
      class DelegateHash
        def initialize(target, method_hash)
          @target = target
          @method_hash = method_hash.dup
        end
        def [](k)
          @target.send(@method_hash[k])
        end
      end
      
      class A
        def initialize
          @a = DelegateHash.new(self, { 0 => :a })
        end
        def a()
          puts "hello world"
        end
        def b()
          @a[0]
        end
      end
      
      x = A.new
      x.a #=> prints "hello world"
      x.b #=> prints "hello world"
      

      您犯的另一个基本错误是您在任何实例方法之外初始化了@a - 只是在A 的定义内部。这是一个很大的禁忌,因为它不起作用。 请记住,在 ruby​​ 中,一切都是对象,包括类,@ 前缀表示实例 任何对象的变量当前是自我。在实例方法定义中,self 是一个实例 类的。但除此之外,就在类定义内部,self 是类对象 - 所以你定义了 类对象A的一个名为@a的实例变量,A的实例都不能直接访问。

      Ruby 确实有这种行为的原因(如果你知道类实例变量会非常方便 你正在做),但这是一种更高级的技术。

      简而言之,只在initialize方法中初始化实例变量。

      【讨论】:

        【解决方案4】:
        table = {
          :a => 'test',
          :b => 12,
          :c => lambda { "Hallo" },
          :d => def print(); "Hallo in test"; end
        }
        
        puts table[:a]
        puts table[:b]
        puts table[:c].call
        puts table[:d].send( :print )
        

        【讨论】:

          【解决方案5】:

          好吧,你的类中的第一行调用了一个尚不存在的方法。但是,在整个类加载后它甚至都不存在,因为那将是对类方法的调用,而您只定义了实例方法。

          还请记住,{0 => a()} 将调用方法 a(),而不是创建对方法 a() 的引用。如果您想在其中放置一个直到稍后才会被评估的函数,您必须使用某种 Lambda。

          【讨论】:

          • 啊,我没有意识到这一点。那么,有没有办法实现我想要做的事情?
          • 你可以这样做:@a = {0 => lambda{A.new.a()}} 然后你必须用@a[0].call 调用它跨度>
          【解决方案6】:

          我对在 Ruby 中使用回调非常陌生,这就是我使用示例向自己解释它的方式:

          require 'logger'
          log = Logger.new('/var/tmp/log.out')
          
          def callit(severity, msg, myproc)
            myproc.call(sev, msg)
          end
          
          lookup_severity = {}
          lookup_severity['info'] = Proc.new { |x| log.info(x) }
          lookup_severity['debug'] = Proc.new { |x| log.debug(x) }
          
          logit = Proc.new { |x,y| lookup_sev[x].call(y) }
          
          callit('info', "check4", logit)
          callit('debug', "check5", logit)
          

          【讨论】:

            【解决方案7】:

            a = ->(string="没有传递字符串") 做

            放字符串

            结束

            some_hash = { 0 => 一个 }

            some_hash[0].call("Hello World")

            some_hash[0][]

            【讨论】:

              猜你喜欢
              • 2011-02-05
              • 1970-01-01
              • 2012-05-12
              • 2017-06-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多