通过Hash的自动化构建寻求动态方法调度

Seeking dynamic method dispatch via automated construction of Hash

我建立随机模拟模型。状态转换由事件方法处理,但发生的顺序因事件的随机性而异,因此我需要某种形式的动态调度。在 Ruby 中使用 send 十多年来,这对我来说效果很好,但我被 Crystal 的速度所吸引,所以我正在尝试构建一个框架来构建模拟Crystal线路。

模型的本质是事件方法根据随机条件触发其他事件的特定子集(并且在随机时间延迟之后,但我已经涵盖了),所以事件确实需要由用户识别- 某种用于调度目的的可读标签。我选择了使用 Hash 将事件标识符映射到它们对应的 Proc 的方法,但我想将模型逻辑与事件调用分开,如下面的“模型”class和模块。 (请注意,实际代码会比这更复杂,事件方法通过优先级队列以随机延迟安排后续事件。这意味着 MCVE 仅在问题的动态按名称分配方面.)

模特:

require "./my_module"

class UserClass
  include MyModule

  def initialize
    @method_set = {
      :m1 => ->m1,
      :m2 => ->m2,
      :m3 => ->m3
    }
    p methods
  end

  def m1
    puts "In method1"
  end

  def m2
    puts "In method2"
  end

  def m3
    puts "In method3"
  end
end

UserClass.new.run(5)

运行所有型号通用的控制逻辑:

module MyModule
  # would like to replace this with a macro that
  # automates construction of the hash of procs.
  macro methods
    {{ @type.methods.map &.name.stringify }}
  end

  @method_set = {} of Symbol => Proc(Nil)

  def run(iterations)
    keys = @method_set.keys
    iterations.times { @method_set[keys[rand(keys.size)]].call }
  end
end

现在开始真正的问题。在严肃的模型中,可能有几十个或数百个事件方法,所以我想使用宏来自动构建 Hash。我做了一些研究,发现了如何通过宏获取方法名称,但似乎找不到正确的咒语来吐出 Symbol 驱动的 Hash 就像我手动创建的那样在构造函数中。对于替代方法的任何帮助或建议,我们将不胜感激。

由于您使用符号作为查找键,我假设您实际上不需要在运行时按名称查找方法。看来您可能只需要一种方法来引用方法。

为此,您可以简单地传递 proc,而无需通过符号进行任何间接访问:

module MyModule
  macro methods
    [{{ @type.methods.map{|method| "-> #{method.name}"}.join(",").id }}]
  end

  def run(iterations)
    iterations.times { methods.sample.call }
  end
end

如果您需要能够完全动态地从 user-defined 字符串(例如 "m1" )发送到 UserClass#m1 (这对于符号键是不可能的,所以我不不认为你这样做),我会简单地使用 macro-generated case 与直接方法调用匹配名称,而不是哈希查找和过程。

更新: 如果您需要实际名称解析,可以使用以下宏生成从方法名称到过程的哈希映射:

module MyModule
  macro methods
    {
      {% for method in @type.methods %}
        {{method.name.stringify}} => -> {{method.name}},
      {% end %}
    }
  end
end