如何在类变量中定义的lambda/Proc中使用实例变量?

4
我写了以下代码:
class Actions
  def initialize
    @people = []
    @commands = {
      "ADD" => ->(name){@people << name },
      "REMOVE" => ->(n=0){ puts "Goodbye" },
      "OTHER" => ->(n=0){puts "Do Nothing" }
    }
  end
  def run_command(cmd,*param)
    @commands[cmd].call param if @commands.key?(cmd)
  end
  def people
    @people
  end
end
act = Actions.new

act.run_command('ADD','joe')
act.run_command('ADD','jack')
puts act.people

这段代码有效,但当@commands散列成为类变量时,散列内部的代码无法访问@people数组。
我该如何使@commands散列成为类变量,同时仍能够访问特定对象实例变量?

1
只是好奇为什么不将 addremoveother 方法定义为实例方法,然后使用 respond_to?send 来调用它们呢? - Victor Moroz
你为什么希望@commands成为一个类变量? - Andrew Grimm
@Victor:一个好的理由是它使访问控制更容易。如果您使用send和方法,您需要一个单独的列表,其中包含允许run_command使用哪些方法,使用哈希将可用命令及其实现收集到一个漂亮整洁的包中。 - mu is too short
@mu 你不能只问一下 respond_to?(cmd) 吗?为什么需要一个单独的允许命令列表? - Marek Příhoda
@maprihoda:如果你不想让别人说 run_command('people') 怎么办?你可以在所有可运行 run_command 的东西前面加上,比如说 cmd_,然后在检查 respond_to? 之前添加这个前缀,从而获得类似的效果。 - mu is too short
显示剩余2条评论
2个回答

6

您可以使用 instance_exec 为调用 Lambda 提供适当的上下文,查看注释以了解更改:

class Actions
  # Move the lambdas to a class variable, a COMMANDS constant
  # would work just as well and might be more appropriate.
  @@commands = {
    "ADD"    => ->(name)  { @people << name   },
    "REMOVE" => ->(n = 0) { puts "Goodbye"    },
    "OTHER"  => ->(n = 0) { puts "Do Nothing" }
  }
  def initialize
    @people = [ ]
  end
  def run_command(cmd, *param)
    # Use instance_exec and blockify the lambdas with '&'
    # to call them in the context of 'self'. Change the
    # @@commands to COMMANDS if you prefer to use a constant
    # for this stuff.
    instance_exec(param, &@@commands[cmd]) if @@commands.key?(cmd)
  end
  def people
    @people
  end
end

啊哈!“参数作为块参数传递”到instance_exec。 - Marek Příhoda

1

编辑 根据@VictorMoroz和@mu的建议:

class Actions
  def initialize
    @people = []
  end

  def cmd_add(name)
    @people << name
  end

  def cmd_remove
    puts "Goodbye"
  end

  def cmd_other
    puts "Do Nothing"
  end

  def people
    p @people
  end

  def run_command(cmd, *param)
    cmd = 'cmd_' + cmd.to_s.downcase
    send(cmd, *param) if respond_to?(cmd)
  end
end

act = Actions.new

act.run_command('add', 'joe')
act.run_command(:ADD, 'jill')
act.run_command('ADD', 'jack')

act.run_command('people') # does nothing

act.people

或者

class Actions
  ALLOWED_METHODS = %w( add remove other )

  def initialize
    @people = []
  end

  def add(name)
    @people << name
  end

  def remove
    puts "Goodbye"
  end

  def other
    puts "Do Nothing"
  end

  def people
    p @people
  end

  def run_command(cmd, *param)
    cmd = cmd.to_s.downcase
    send(cmd, *param) if ALLOWED_METHODS.include?(cmd)
  end
end

act = Actions.new

act.run_command('add', 'joe')
act.run_command(:add, 'jill')
act.run_command('add', 'jack')

act.run_command('people') # does nothing

act.people

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接