【问题标题】:Ruby dynamic arguments in dynamically created methods动态创建的方法中的 Ruby 动态参数
【发布时间】:2013-03-29 21:39:11
【问题描述】:

我有以下几种方法定义:

method_name = :foo
method_arguments = [:bar, :baz]
method_mandatory_arguments = {:quux => true}
method_body = ->{ quux ? bar + baz : bar - baz }

所以我想得到一个真正的方法。但是define_method 不可能动态定义方法参数。我知道使用class_eval 的另一种方法,但我知道使用class_eval 定义方法比define_method 慢得多。 我怎样才能有效地归档这个?

我在 rails 控制台做了一些基准测试:

class Foo; end
n = 100_000

Benchmark.bm do |x|
  x.report('define_method') do
    n.times { |i| Foo.send(:define_method, "method1#{i}", Proc.new { |a, b, c| a + b + c }) }
  end
  x.report('class_eval') do
    n.times { |i| Foo.class_eval %Q{ def method2#{i}(a, b, c); a + b + c; end } }
  end
end

所以我得到了以下结果:

                user       system     total      real
define_method  0.750000   0.040000   0.790000 (  0.782988)
class_eval     9.510000   0.070000   9.580000 (  9.580577)

【问题讨论】:

  • class_eval 对我来说很有意义。性能真的是这里的问题吗?很难想象它会有一个用例。
  • 您可以让您的 define_method 块接受可变参数,然后根据您在闭包中捕获的参数元数据解释 args 数组。
  • @dbenhur,好的,但在这种情况下,实际上我有一种实现乘法行为的方法,所以我必须手动实现基本的参数行为(参数存在、默认参数等)。这是我试图避免的。
  • 你确定 class_eval 对这类东西比较慢吗?我的回忆是因为 define_method 创建了一个闭包,它可能会更慢(或导致泄漏)
  • 鉴于 method_body 是一个没有参数的 lambda,您能否向我们展示您使用 class_eval 的实现?

标签: ruby dynamic metaprogramming dynamic-programming


【解决方案1】:

没有简单的方法可以使用 class_eval 或 define_method 来实现您的要求。由于 method_body 是一个 lambda,它只能访问在它之前定义的局部变量。

method_body = ->{ quux ? bar + baz : bar - baz }
quux = true
method_body.call # undefined local variable or method ‘quux’

quux = true
method_body = ->{ quux ? bar + baz : bar - baz }
method_body.call # undefined local variable or method ‘bar’

我建议您修改您的要求。如果您的方法主体应该是 lambda,请为其定义所有参数。那么这很适合 define_method:

method_body = ->(quux, bar = 0, baz = 0){ quux ? bar + baz : bar - baz }
define_method(method_name, &method_body)

如果你的方法体可以是字符串,那么 eval 是唯一的选择:

method_body = "quux ? bar + baz : bar - baz"
eval <<RUBY
def #{method_name} #{arguments}
  #{method_body}
end
RUBY

【讨论】:

  • 从您的角度来看,我想使用我以特定形式存储的一组参数创建一个 proc(或在我的情况下无关紧要的 lambda)。所以我有一个数组method_arguments = [:bar, :baz],我想像这样使用它:method_body = -&gt;(*method_arguments){ quux ? bar + baz : bar - baz }
  • 这对于 procs/lambdas 是不可能的。您可以将方法主体存储为字符串吗?如果您提供更具体的真实示例,将会有所帮助。
  • 看来只能用class_eval了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2021-12-05
  • 2016-08-31
  • 2011-01-14
  • 1970-01-01
  • 2016-11-09
  • 2020-02-22
相关资源
最近更新 更多