Answers:
这个问题在Ruby邮件列表和Ruby博客上讨论得如此频繁,以至于Ruby邮件列表上甚至还有一些线程,其唯一目的是收集指向讨论该问题的Ruby邮件列表上所有其他线程的链接。。
这是一个:|| =(或等于)线程和页面的确定列表
如果您真的想知道发生了什么,请查看Ruby语言草案规范的 11.4.2.3节“缩写的赋值” 。
作为第一近似,
a ||= b
相当于
a || a = b
并且不等同于
a = a || b
但是,这只是一个近似值,尤其a
是在未定义的情况下。语义也有所不同,取决于它是简单变量分配,方法分配还是索引分配:
a ||= b
a.c ||= b
a[c] ||= b
都被不同地对待。
a = false; a ||= true
并没有做你的答案说,它的作用是“细微差别”。
a ||= b
是条件赋值运算符。这意味着如果a
未定义或为假,则求值b
并设置a
为结果。等效地,如果a
被定义并评估为真,b
则不评估,并且不会发生分配。例如:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
令人困惑的是,它看起来与其他赋值运算符(例如 +=
),但是行为却有所不同。
a += b
转换为 a = a + b
a ||= b
大致翻译成 a || a = b
这是的简写a || a = b
。区别在于,当a
未定义时,a || a = b
将引发NameError
,而a ||= b
将设置a
为b
。这种区分不重要,如果a
和b
都是局部变量,,但如果两者都是类的getter / setter方法,。
进一步阅读:
h = Hash.new(0); h[1] ||= 2
。现在考虑两个可能的扩展h[1] = h[1] || 2
VS h[1] || h[1] = 2
。这两个表达式求和,0
但第一个表达式不必要地增加了散列的大小。也许这就是Matz选择使||=
行为更像第二次扩展的原因。(我基于一个链接到另一个答案的线程之一的示例。)
a || a = b
引发NameError
if如果a
未定义。a ||= b
不会,而是初始化a
并将其设置为b
。据我所知,这是两者之间的唯一区别。同样,a = a || b
和a ||= b
我知道的唯一区别是,如果a=
是方法,则无论a
返回什么,都将调用它。此外,唯一的区别a = b unless a
和a ||= b
我所知道的那就是该语句的计算结果为nil
,而不是a
如果a
是truthy。很多近似值,但没有什么等同的...
a ||= b
评估方式同每个以下的行
a || a = b
a ? a : a = b
if a then a else a = b end
--
另一方面,
a = a || b
评估方式同每个以下的行
a = a ? a : b
if a then a = a else a = b end
--
编辑:正如AJedi32在注释中指出的那样,仅在以下情况下成立:1. a是定义的变量。2.评估一次和两次不会导致程序或系统状态的差异。
a
为false /零/ undefined,则对其进行两次评估。(但是我不了解Ruby,所以不知道是否可以精确地“评估”左值...)
a || a = b
,a ? a : a = b
,if a then a else a = b end
,和if a then a = a else a = b end
将抛出一个错误,如果a
是不确定的,而a ||= b
并a = a || b
不会。此外,a || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
,和if a then a = a else a = b end
评估a
时两次a
是truthy,而a ||= b
并a = a || b
没有。
a || a = b
则不会进行a
两次评估a
。
the end state will be equivalent after the whole line has been evaluated
虽然不一定如此。如果a
是方法怎么办?方法可能有副作用。例如用public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
,self.a ||= b
将返回6,但self.a ? self.a : self.a = b
将返回7.
等于或等于。它检查是否已定义左侧的值,然后使用该值。如果不是,请使用右侧的值。您可以在Rails中使用它在模型中缓存实例变量。
一个基于Rails的快速示例,我们在其中创建一个函数来提取当前登录的用户:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
它检查是否设置了@current_user实例变量。如果是,它将返回它,从而保存数据库调用。如果未设置,则进行调用,然后将@current_user变量设置为该值。这是一种非常简单的缓存技术,但是当您多次从应用程序中获取相同的实例变量时非常有用。
undefined
,而且还会触发false
和nil
,这可能与无关current_user
,尤其false
是在其他情况下可能会意外发生
x ||= y
是
x || x = y
“如果x为false或未定义,则x指向y”
确切地说,a ||= b
表示“如果a
未定义或错误(false
或nil
),则设置a
为b
并求值为(即返回)b
,否则为a
”。
其他人常常试图通过说a ||= b
等于a || a = b
或来说明这一点a = a || b
。这些等效项可能有助于理解概念,但请注意,它们并非在所有情况下都是准确的。请允许我解释一下:
a ||= b
⇔a || a = b
?
当a
是未定义的局部变量时,这些语句的行为会有所不同。在这种情况下,a ||= b
将设置a
为b
(并评估为b
),而a || a = b
将提高NameError: undefined local variable or method 'a' for main:Object
。
a ||= b
⇔a = a || b
?
这些语句的等效常常假设,因为类似的等价是其他真正的简写赋值运算符(即+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
,和>>=
)。但是,由于||=
这些语句的行为在对象上的方法为真时可能会有所不同。在这种情况下,不会做任何事情(比评估为其他),而将调用上的接收器。正如其他人指出的那样,当调用具有副作用(例如将键添加到哈希)时,这可能会有所作为。a=
a
a ||= b
a
a = a || b
a=(a)
a
a=a
a ||= b
⇔a = b unless a
??
这些陈述的行为仅在它们为真时的评估上有所不同a
。在这种情况下,a = b unless a
将评估为nil
(尽管a
仍未按预期设置),而a ||= b
将评估为a
。
a ||= b
⇔ defined?(a) ? (a || a = b) : (a = b)
????
仍然没有。当method_missing
存在返回真值的方法时,这些语句可能会有所不同a
。在这种情况下,a ||= b
将评估为任何method_missing
收益,而不会尝试设置a
,而defined?(a) ? (a || a = b) : (a = b)
将设置a
为b
并评估为b
。
好吧好吧 是 a ||= b
相当于?有没有办法用Ruby表达这一点?
好吧,假设我没有忽略任何东西,我相信a ||= b
它在功能上等同于...(drumroll)
begin
a = nil if false
a || a = b
end
坚持,稍等!难道这不是第一个没有noop的例子吗?好吧,不完全是。还记得我之前所说的a ||= b
不等同于a || a = b
何时a
是未定义的局部变量吗?好吧,即使从不执行该行,也要a = nil if false
确保它a
永远不会被定义。Ruby中的局部变量在词法范围内。
(a=b unless a) or a
a
是方法,它将被调用两次而不是一次(如果它是第一次返回真实值)。例如,如果a
返回时间较长或有副作用,则可能导致行为不同。
b
toa
,还是rhs仍然没有分配给lhs,或者换句话说,lhs仍然没有将值设置为rhs吗?
a ||= b
我在互联网上找到的最佳答案。谢谢。
假设 a = 2
和b = 3
然后,a ||= b
将成为a
的值,即2
。
就像当a评估某个值时未产生false
或nil
..这就是为什么它ll
不评估b
的值。
现在假设 a = nil
和b = 3
。
然后a ||= b
将得出 3
e b
的值。
由于它首先尝试评估结果为nil
..的a的值,因此它评估了b
的值。
在ror应用中使用的最佳示例是:
#To get currently logged in iser
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
在,User.find_by_id(session[:user_id])
且仅当@current_user
之前未初始化时才触发。
a || = b
表示“ a”中是否存在任何值,并且您不想使用该值更改保持值,否则,如果“ a”不具有任何值,则使用值“ b”。
简单的单词,如果左侧不为null,则指向现有值,否则指向右侧的值。
还请记住,这||=
不是原子操作,因此它不是线程安全的。根据经验,请勿将其用于类方法。
此ruby-lang语法。正确的答案是检查ruby-lang文档。所有其他解释都难以理解。
“ ruby-lang docs缩写作业”。
https://docs.ruby-lang.org/zh/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
转换为:
a = a || b
这将是
a = nil || 5
所以最后
a = 5
现在,如果再次调用此命令:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
现在,如果再次调用此命令:
a ||= b
a = a || b
a = 5 || 6
a = 5
如果您观察到,b
值将不会分配给a
。a
仍然会有5
。
它是一种在Ruby中用于加速访问器的记忆模式。
def users
@users ||= User.all
end
这基本上转化为:
@users = @users || User.all
因此,您将在首次调用此方法时对数据库进行调用。
将来对该方法的调用将仅返回@users
实例变量的值。
a ||= b
与说a = b if a.nil?
或a = b unless a
但是,这三个选项是否都显示相同的性能?使用Ruby 2.5.1,
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
我的电脑需要0.099秒,而
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
需要0.062秒。快了将近40%。
然后我们还有:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
这需要0.166秒。
并不是说这通常会对性能产生重大影响,但是如果您确实需要最后一点优化,则可以考虑此结果。顺便说一句:a = 1 unless a
对于新手来说更容易阅读,这是不言而喻的。
注1:多次重复分配行的原因是为了减少所测量时间的环路开销。
注意2:如果我a=nil
在每次作业前都没有设置nil,结果将是相似的。