|| =(等于)在Ruby中是什么意思?


340

以下代码在Ruby中意味着什么?

||=

语法有任何含义或原因吗?

Answers:


175

这个问题在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

都被不同地对待。


2
第二个链接遭受了位腐烂(stackoverflow.com/users/540162/nightfirecat从meta中发表的评论)。
安德鲁·格林

330
那是一个非常神秘的非答案。简短的答案似乎是:a || = b表示,如果a未定义,则为其分配b的值,否则将其保留。(好的,有细微差别和特殊情况,但这是基本情况。)
史蒂夫·本内特

20
@SteveBennett:我不认为这是一个事实a = false; a ||= true没有做你的答案说,它的作用是“细微差别”。
约尔格W¯¯米塔格

23
也许这个问题被问过很多次,因为人们一直在回答,这个问题已经被问过很多次了。
einnocent

8
有了这个答案,很容易看出为什么有多个线程。如果尝试使用新手帽子搜索此问题的答案,您会注意到所有答案都不清楚。例如,对于这个,您只是在说什么不是。我建议改善您的答案,并为新手提供简单的答案:a = b,除非a
Arnold Roa 2015年

594

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将设置ab。这种区分不重要,如果ab都是局部变量,,但如果两者都是类的getter / setter方法,。

进一步阅读:


52
谢谢您的回答,这更有意义。
2013年

还没有足够的搜索,但是仍然不知道为什么要使用它而不是a = a || b。也许只是我个人的看法,但是存在这种细微差别有点荒谬……
dtc

2
@dtc,考虑h = Hash.new(0); h[1] ||= 2。现在考虑两个可能的扩展h[1] = h[1] || 2VS h[1] || h[1] = 2。这两个表达式求和,0但第一个表达式不必要地增加了散列的大小。也许这就是Matz选择使||=行为更像第二次扩展的原因。(我基于一个链接到另一个答案的线程之一的示例。)
antinome 2014年

1
我喜欢其他答案的深入程度,但由于它的简单性,我喜欢这个答案。对于学习Ruby的人来说,这是我们需要的答案类型。如果我们知道|| =的含义,那么问题的措词可能会有所不同。
OBCENEIKON 2014年

1
Fyi,a || a = b引发NameErrorif如果a未定义。a ||= b不会,而是初始化a并将其设置为b。据我所知,这是两者之间的唯一区别。同样,a = a || ba ||= b我知道的唯一区别是,如果a=是方法,则无论a返回什么,都将调用它。此外,唯一的区别a = b unless aa ||= b我所知道的那就是该语句的计算结果为nil,而不是a如果a是truthy。很多近似值,但没有什么等同的...
Ajedi32

32

简洁完整的答案

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.评估一次和两次不会导致程序或系统状态的差异。


1
你确定吗?这意味着,如果a为false /零/ undefined,则对其进行两次评估。(但是我不了解Ruby,所以不知道是否可以精确地“评估”左值...)
史蒂夫·贝内特

我明白你在说什么。我所说的两行等效是指在评估了整行之后,最终状态将等效,这意味着a,b的值以及返回的值。红宝石解释器是否使用不同的状态(如对a的多个评估)才能达到目标。那里有红宝石翻译专家吗?
the_minted 2013年

3
这不太正确。a || a = ba ? a : a = bif a then a else a = b end,和if a then a = a else a = b end将抛出一个错误,如果a是不确定的,而a ||= ba = a || b不会。此外,a || a = ba ? a : a = bif a then a else a = b enda = a ? a : b,和if a then a = a else a = b end评估a时两次a是truthy,而a ||= ba = a || b没有。
2014年

1
*更正:如果为true,a || a = b则不会进行a两次评估a
Ajedi32

1
@the_minted 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 = 5self.a ||= b将返回6,但self.a ? self.a : self.a = b将返回7.
Ajedi32

27

简而言之,a||=b意味着:如果aundefined, nil or false,则分配ba。否则,请保持a原样。


16
基本上,


x ||= y 手段

如果x有任何值,请不要管它,不要更改值,否则设置xy


13

等于或等于。它检查是否已定义左侧的值,然后使用该值。如果不是,请使用右侧的值。您可以在Rails中使用它在模型中缓存实例变量。

一个基于Rails的快速示例,我们在其中创建一个函数来提取当前登录的用户:

class User > ActiveRecord::Base

  def current_user
    @current_user ||= User.find_by_id(session[:user_id])
  end

end

它检查是否设置了@current_user实例变量。如果是,它将返回它,从而保存数据库调用。如果未设置,则进行调用,然后将@current_user变量设置为该值。这是一种非常简单的缓存技术,但是当您多次从应用程序中获取相同的实例变量时非常有用。


8
错了 请阅读Ruby-Forum.Com/topic/151660及其提供的链接。
约尔格W¯¯米塔格

1
@Jo(umlaut)rg,我看不出有什么问题。您的链接是其他链接的列表。没有真正的解释为什么会出错,只是听起来像是对您的价值判断。
eggmatters 2014年

这个答案是错误的,因为它不仅会触发undefined,而且还会触发falsenil,这可能与无关current_user,尤其false是在其他情况下可能会意外发生
dfherr

尽管此答案可能会出现不完整的情况(不适用于nil / false),但它还是第一个解释了为什么要使用|| =的原因,谢谢!
乔纳森·图兹曼


8

确切地说,a ||= b表示“如果a未定义或错误(falsenil),则设置ab并求值为(即返回)b,否则为a”。

其他人常常试图通过说a ||= b等于a || a = b或来说明这一点a = a || b。这些等效项可能有助于理解概念,但请注意,它们并非在所有情况下都是准确的。请允许我解释一下:

  • a ||= ba || a = b

    a是未定义的局部变量时,这些语句的行为会有所不同。在这种情况下,a ||= b将设置ab(并评估为b),而a || a = b将提高NameError: undefined local variable or method 'a' for main:Object

  • a ||= ba = a || b

    这些语句的等效常常假设,因为类似的等价是其他真正的简写赋值运算符(即+=-=*=/=%=**=&=|=^=<<=,和>>=)。但是,由于||=这些语句的行为在对象上的方法为真时可能会有所不同。在这种情况下,不会做任何事情(比评估为其他),而将调用上的接收器。正如其他人指出的那样,当调用具有副作用(例如将键添加到哈希)时,这可能会有所作为。a=aa ||= baa = a || ba=(a)aa=a

  • a ||= ba = b unless a ??

    这些陈述的行为仅在它们为真时的评估上有所不同a。在这种情况下,a = b unless a将评估为nil(尽管a仍未按预期设置),而a ||= b将评估为a

  • a ||= bdefined?(a) ? (a || a = b) : (a = b) ????

    仍然没有。当method_missing存在返回真值的方法时,这些语句可能会有所不同a。在这种情况下,a ||= b将评估为任何method_missing收益,而不会尝试设置a,而defined?(a) ? (a || a = b) : (a = b)将设置ab并评估为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
vol7ron 2015年

1
@ vol7ron与#2有类似的问题。如果a是方法,它将被调用两次而不是一次(如果它是第一次返回真实值)。例如,如果a返回时间较长或有副作用,则可能导致行为不同。
Ajedi32

另外,第一句话,不是应该说assign btoa,还是rhs仍然没有分配给lhs,或者换句话说,lhs仍然没有将值设置为rhs吗?
vol7ron 2015年

a ||= b我在互联网上找到的最佳答案。谢谢。
埃里克·杜米尼尔

3

unless x x = y end

除非x具有值(不是nil或false),否则将其设置为y

相当于

x ||= y


3

假设 a = 2b = 3

然后,a ||= b 将成为a的值,即2

就像当a评估某个值时未产生falsenil..这就是为什么它ll不评估b的值。

现在假设 a = nilb = 3

然后a ||= b将得出 3e 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之前未初始化时才触发。


3

a || = b

表示“ a”中是否存在任何值,并且您不想使用该值更改保持值,否则,如果“ a”不具有任何值,则使用值“ b”。

简单的单词,如果左侧不为null,则指向现有值,否则指向右侧的值。


2
a ||= b

相当于

a || a = b

并不是

a = a || b

由于您使用默认值定义哈希的情况(哈希将返回所有未定义键的默认值)

a = Hash.new(true) #Which is: {}

如果您使用:

a[10] ||= 10 #same as a[10] || a[10] = 10

一个仍然是:

{}

但是当你这样写的时候:

a[10] = a[10] || 10

变成:

{10 => true}

因为您已经在key处分配了self的值10,默认为true,所以现在为key定义了哈希10,而不是从不首先执行分配。


2

就像懒惰的实例化。如果已经定义了变量,它将使用该值,而不是再次创建该值。



2

这是默认的分配符号

例如:x || = 1
这将检查x是否为nil。如果x确实为nil,它将为它分配新值(在我们的示例中为1)

更明确:
如果x == nil
x = 1
结束


无论是nilfalse,不仅nil
亚历犯罪所得

2

|| =条件赋值运算符

  x ||= y

相当于

  x = x || y

或者

if defined?(x) and x
    x = x
else 
    x = y
end

2

如果X没有值,则会为分配值Y。否则,它将保留其原始值,在本示例中为5:

irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5

# Now set x to nil. 

irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10

1

作为一种常见的误解,a ||= b它并不等同于a = a || b,但其行为类似于a || a = b

但是这是一个棘手的案例。如果a未定义,则a || a = 42引发NameError,而a ||= 42返回42。因此,它们似乎不是等效的表达式。




0
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1

因为a已经设置为1

irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2

因为anil


答案是什么?为什么不显示年份?
Shiv

0
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值将不会分配给aa仍然会有5

它是一种在Ruby中用于加速访问器的记忆模式。

def users
  @users ||= User.all
end

这基本上转化为:

@users = @users || User.all

因此,您将在首次调用此方法时对数据库进行调用。

将来对该方法的调用将仅返回@users实例变量的值。


0

||= 被称为条件赋值运算符。

基本上,它的工作原理是,=但例外的是,如果已经分配了变量,它将什么也不做。

第一个例子:

x ||= 10

第二个例子:

x = 20
x ||= 10

x现在在第一个示例中等于10。但是,在第二个示例x中已经定义为20。因此,条件运算符无效。x跑步后仍然20岁x ||= 10


-2

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,结果将是相似的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.