在实例方法中分配对象变量

时间:2016-08-30 17:23:00

标签: ruby ruby-on-rails-4

我经常使用|| =运算符来减少冗余调用。我想采取这样的东西

@my_variable ||= my_calculation_method

并将其转换为

@my_variable.assign { code_block }

使用这个想法

class Object
    def assign
      if self.instance_of? NilClass
          self = yield
      end
    end
end

正如您可能已经猜到的那样,分配self没有意义且不起作用。

如何访问@my_variable方法中的assign指针来修改值?

3 个答案:

答案 0 :(得分:5)

如果你的初始化逻辑是非常重要的并跨越几行(我认为这是问题的前提),那么你可以做什么,用惯用的ruby:

@my_variable ||= begin
  # code block
end

我通常做的是将逻辑拆分为两部分。记忆与计算分开。你的第一行就是这个。

def stats
  @stats ||= compute_stats
end

def compute_stats
  ...
end

我发现XXX + compute_XXX的这种模式很容易识别/跟随。

答案 1 :(得分:3)

self = x完全不可能。你无法告诉对象它是什么,永远不会改变。如果它是用特定的类创建的,那么它会与同一个类一起死掉。您所能做的就是修改该对象或其关联的类,但该对象的类是不可变的。

@x ||= y工作的唯一原因是因为@x只是一个变量,它是一个指向对象的指针,这是你可以重新分配的东西。说@x = z不会改变对象,它只会改变指针。 self是一种特殊情况,它指的是您在其中运行的上下文,并且无法重新分配。

请注意,如果您的self = yield来电成功,您将重新定义nil 程序范围的内容。

这整个努力都是错误的,||=模式在这一点上几乎是惯用的Ruby。如果你想改进那种模式,那么你想做的就是制作一些alternative to memoize

答案 2 :(得分:2)

正如塔德曼所说,你不能写self =。但是你可以编写一个方法,它接受实例变量的名称并设置它。

你有评论说这是'不优雅'或'误入歧途'。虽然我不同意这种判断,但优良作法是编写尽可能使用标准约定的不起眼的代码。这有助于其他人轻松理解您的代码。即为||=这样的基本ruby方法编写替代语法可能效率不高;其他人将不得不大量挖掘您的代码以了解事情的运作方式。

话虽如此,为了教会如何让Ruby做你想做的事,我可以为此建议这个Object方法:

class Object
    def assign(key)
      # make sure the key is a valid instance variable
      unless key =~ /^[A-Za-z0-9\_]+$/ 
        raise ArgumentError, "#{key} is not a valid ivar name"
      end

      if instance_variable_get("@#{key}").nil?
        instance_variable_set("@#{key}", yield)
      end
    end
end

# example
class Foo
  def set_bsd
    assign("bsd", yield)
  end
end
foo = Foo.new
foo.assign("my_ivar") { "val" }
foo
# => #<Foo:0x007fc2e4ccaca0 @asd=1>
foo.set_bsd { 2 }
foo
# => #<Foo:0x007fc2e4ccaca0 @asd=1 @bsd = 2>

虽然我建议不要以这种方式修补Object - 它太普通了,并且可能与内部功能发生意外冲突。而是将此功能添加到自定义类/模块,并include模块或继承该类。