如何在Ruby中记忆可能返回true,false或nil的方法?

Sea*_*ere 16 ruby null memoization

显然||=不行

def x?
  @x_query ||= expensive_way_to_calculate_x
end
Run Code Online (Sandbox Code Playgroud)

因为如果它原来是falsenil,然后expensive_way_to_calculate_x将得到反复执行.

目前我知道的最好方法是将值放入Array:

def x?
  return @x_query.first if @x_query.is_a?(Array)
  @x_query = [expensive_way_to_calculate_x]
  @x_query.first
end
Run Code Online (Sandbox Code Playgroud)

有更传统或更有效的方法吗?

更新我意识到我想要记忆nil除了false- 这一直回到https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean - 缓存价值 - 我向安德鲁马歇尔道歉,他给出了一个完全正确的答案.

And*_*all 27

明确检查值是否@x_querynil:

def x?
  @x_query = expensive_way_to_calculate_x if @x_query.nil?
  @x_query
end
Run Code Online (Sandbox Code Playgroud)

请注意,如果这不是实例变量,则必须检查它是否也被定义,因为所有实例变量都默认为nil.

鉴于您的更新@x_query可以是memoized值nil,您可以使用它defined?来解决所有实例变量默认为nil:

def x?
  defined?(@x_query) or @x_query = expensive_way_to_calculate_x
  @x_query
end
Run Code Online (Sandbox Code Playgroud)

请注意,a = 42 unless defined?(a)一旦解析器命中a =,执行类似的操作将无法按预期工作,它到达条件之前a定义.但是,实例变量不是这样,因为它们默认为解析器在命中时不会定义它们.无论如何,我认为这是一个很好的成语使用或代替一个线的长块的形式以使其保持一致.nil=orunlessunlessdefined?

  • `a = 42除非定义?(a)`make`a`nil,但`@a = 42除非定义?(@ a)`make` @ a`42,所以`or`语法不是绝对必需的这个例子. (3认同)

mea*_*gar 21

要考虑nil,请使用defined?以查看变量是否已定义:

def x?
  return @x_query if defined? @x_query
  @x_query = expensive_way_to_calculate_x
end
Run Code Online (Sandbox Code Playgroud)

defined?nil如果尚未定义变量,则返回,否则返回字符串"instance_variable":

irb(main):001:0> defined? @x
=> nil
irb(main):002:0> @x = 3
=> 3
irb(main):003:0> defined? @x
=> "instance-variable"
Run Code Online (Sandbox Code Playgroud)