通过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
我建立随机模拟模型。状态转换由事件方法处理,但发生的顺序因事件的随机性而异,因此我需要某种形式的动态调度。在 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