如何在 Ruby 中记忆一个可能返回 true、false 或 nil 的方法?
Posted
技术标签:
【中文标题】如何在 Ruby 中记忆一个可能返回 true、false 或 nil 的方法?【英文标题】:How can I memoize a method that may return true, false, or nil in Ruby? 【发布时间】:2012-06-24 21:59:25 【问题描述】:显然||=
不起作用
def x?
@x_query ||= expensive_way_to_calculate_x
end
因为如果结果是false
或nil
,那么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
有没有更传统或更有效的方法来做到这一点?
更新我意识到除了false
之外我还想记住nil
- 这一直追溯到https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value - 我向Andrew Marshall道歉正确答案。
【问题讨论】:
这就是nil
和null
的整个概念的用途。
我编辑了我的问题,因为我第一次没有做对 - 我也想记住 nil。
【参考方案1】:
明确检查@x_query
的值是否为nil
:
def x?
@x_query = expensive_way_to_calculate_x if @x_query.nil?
@x_query
end
请注意,如果这不是实例变量,则您必须检查它是否也/而不是定义,因为所有实例变量默认为 nil
。
鉴于您更新@x_query
的记忆值可以是nil
,您可以使用defined?
来解决所有实例变量默认为nil
的事实:
def x?
defined?(@x_query) or @x_query = expensive_way_to_calculate_x
@x_query
end
请注意,执行类似a = 42 unless defined?(a)
的操作不会按预期工作,因为一旦解析器命中a =
,a
就被定义在到达条件之前。但是,实例变量并非如此,因为它们默认为nil
,解析器在遇到=
时不会定义它们。无论如何,我认为使用or
或unless
的长块形式而不是单行unless
和defined?
保持一致是一个很好的习惯用法。
【讨论】:
我要对此表示赞同,因为它是我问题第一个版本的答案,但请看看我的编辑——再次道歉。 @SeamusAbshere 我已经更新了我的答案以反映您的更新:)
。
a = 42 unless defined?(a)
使a
为零,但@a = 42 unless defined?(@a)
使@a
为42,因此在此示例中并非绝对需要or
语法。
@AndrewGrimm 嗯,我想我验证了它与实例变量的行为相同,但看来你是正确的。我认为这是因为 @a
已经有一个值,即使它没有被定义。
@SeamusAbshere 我已经更新了我的答案,以反映实例变量影响最后一位的方式。【参考方案2】:
要说明nil
,请使用defined?
查看变量是否已定义:
def x?
return @x_query if defined? @x_query
@x_query = expensive_way_to_calculate_x
end
如果变量尚未定义,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"
【讨论】:
以上是关于如何在 Ruby 中记忆一个可能返回 true、false 或 nil 的方法?的主要内容,如果未能解决你的问题,请参考以下文章
在 Ruby 中,是不是有类似于 `any?` 的方法返回匹配项(而不是 `true`)
Ruby:为什么`puts true和false`返回true? [重复]
如何使用 Python 的 ast 创建类型化的返回? - `def f() -> bool: return True`