【问题标题】:Cleaner way to define methods without lambdas更简洁的方法来定义没有 lambda 的方法
【发布时间】:2012-05-30 08:50:13
【问题描述】:

是否有一种更简单和/或更易读的方法在 Ruby 中创建 闭包,以便定义的方法可以访问变量 m?

我在这里的lambda 有一点“问题”。

我经常动态定义必须访问局部变量的方法:

例如:

class Comparison

  def income
    123
  end

  def sales
    42342
  end

  # and a dozen of other methods

  # Generate xxx_after_tax for each method
  instance_methods(false).each do |m|
    lambda {
      define_method("#{m}_after_tax") do
        send(m) * 0.9
      end
    }.call
  end
end

【问题讨论】:

  • 您确定需要 lambda 吗?没有它,关闭应该可以正常工作。
  • 他在这里不需要 lambda)) 这看起来很有趣,创建 lambda 并立即调用它。 Lambda 只是一个匿名函数。
  • 是的。 Lambda 需要能够访问方法定义中的m 变量。
  • 我不知道你用的是哪个 Ruby,但是 1.8.7 和 1.9.3 都不需要这个 lambda。
  • 忽略它。它实际上可以在没有 lambda 的情况下工作。现在不记得我何时以及为什么开始将其包装在 lambdas 中。

标签: ruby-on-rails ruby closures metaprogramming


【解决方案1】:
class Comparison

  def income
    123
  end

  def sales
    42342
  end

  # and a dozen of other methods

  # Generate xxx_after_tax for each method
  instance_methods(false).each do |m|

    define_method("#{m}_after_tax") do
      send(m) * 0.9
    end

  end
end

【讨论】:

  • 你不能这样做,因为你将无法从实现中访问变量m
  • 你试过了吗?它可以工作......在这里使用 lambda 是多余的,因为你没有将变量传递给 lambda,而且根本没有任何意义))。 define_method 它只是一个你在迭代器内部调用的函数,所以参数可以包含迭代器范围内的任何变量
  • @DmytriiNagirniak 这是正确答案,我的答案中有一个最小的测试用例,您可以自己尝试。
  • 确实如此。我不明白为什么我一直在使用lambda。肯定有需要的情况。不过现在想不起来了……
【解决方案2】:

常规方法定义不是闭包,但在这里您使用块调用define_method,而块闭包。这应该足够了:

instance_methods(false).each do |m|
  define_method :"#{m}_after_tax" do
    send(m) * 0.9
  end
end

【讨论】:

  • 我想我举了一个不好的例子。因此,如果我要从一个方法定义方法,我需要创建一个闭包(使用 lambda 或块)?对吗?
  • @Dmytrii,如果您需要捕获给定上下文中可用的局部变量,则必须创建一个闭包。这里的问题是您正在创建两个:lambda 和给define_method 的块。前者是不必要的。
【解决方案3】:

正如 Yuri 指出的那样,lambda 是多余的,您可以通过运行此示例看到这一点。

#!/usr/bin/env ruby -w

class Foo
  [:foo, :bar].each do |m|
    define_method("#{m}_dynamic") do
      "Called #{m}"
    end
  end
end

p Foo.new.foo_dynamic # => "Called foo"

【讨论】:

    【解决方案4】:
      instance_methods(false).each do |m|
        class_eval <<-ERUBY, __FILE__, __LINE__
          def #{m}_after_tax
            #{m} * 0.9
          end
        ERUBY
      end
    

    【讨论】:

      【解决方案5】:

      你可以像这样使用 method_missing:

      def method_missing(name, *args, &block)
        if name.to_s.match /^([a-z_]+)_after_tax$/
          send($1)
        else
          super
        end
      end
      

      我希望这会有所帮助。

      【讨论】:

      • 根本不需要使用method_missing。它使事情复杂化。您还必须实施respond_to?和相关的。这里绝对是矫枉过正。
      • 您能否详细说明为什么我必须实施 respond_to?
      猜你喜欢
      • 1970-01-01
      • 2013-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-15
      相关资源
      最近更新 更多