||= (or-equals)在Ruby中是什么意思?

下面的代码在Ruby中是什么意思?

||=

它的语法有什么意义或原因吗?

159680 次浏览

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

一个基于rails的快速示例,我们创建了一个函数来获取当前登录的用户:

class User > ActiveRecord::Base


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


end

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

这个问题在Ruby邮件列表和Ruby博客上讨论得如此频繁,以至于现在Ruby邮件列表上甚至有线程,其唯一目的是在Ruby邮件列表上收集到所有其他线程的链接来讨论这个问题。

这里有一个:||= (OR Equal)线程和页面的最终列表

如果你真的想知道发生了什么,请查看第11.4.2.3节“缩写赋值”。的Ruby语言规范草案

作为第一近似,

a ||= b

等于

a || a = b

等价于

a = a || b

然而,这只是一种近似,特别是在a未定义的情况下。根据是简单的变量赋值、方法赋值还是索引赋值,语义也有所不同:

a    ||= b
a.c  ||= b
a[c] ||= b

都有不同的待遇。

a ||= b是一个条件赋值运算符。它的意思是:

  • 如果a为undefined或美甲师,则求值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 ||= ba设置为b。如果ab都是局部变量,这个区别就不重要了,但如果其中一个是类的getter/setter方法,这个区别就很重要了。

进一步阅读:

x ||= y

x || x = y

"如果x为假或未定义,则x指向y"

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仍然是:

{}

但如果你这样写

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

一个变成了:

{10 => true}

因为你已经在键10处赋值了自身的值,默认为true,所以现在哈希是为键10定义的,而不是一开始就不执行赋值。

假设a = 2b = 3

然后,a ||= b将得到a的值,即2

当a计算为某个值而不是falsenil..这就是为什么ll不计算b的值。

现在假设a = nilb = 3

然后a ||= b将被计算为3,即b的值。

因为它首先尝试计算a的值,结果是nil..所以它计算b的值。

ror app中使用的最佳示例是:

#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

其中,当且仅当@current_user之前未初始化时触发User.find_by_id(session[:user_id])

简洁完整的回答

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在评论中指出的那样,这只在以下情况下成立:A是一个已定义变量。2. 计算一次时间和两次时间不会导致程序或系统状态的差异。

这就像延迟实例化。 如果该变量已经定义,则它将获取该值,而不是重新创建该值

简而言之,a||=b意味着:如果aundefined, nil or false,将b赋值给a。否则,保持a不变。

这是默认的赋值表示法

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

更显式:
如果x == nil
X = 1
< / p >结束

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

其他人通常试图通过说a ||= b等价于a || a = ba = a || b来说明这一点。这些等价可以帮助理解概念,但要注意它们在所有条件下都是准确的。请允许我解释:

  • < p > __abc0⇔⇔吗?

    a是一个未定义的局部变量时,这些语句的行为不同。在这种情况下,a ||= ba设置为b(并求值为b),而a || a = b将引发NameError: undefined local variable or method 'a' for main:Object

  • < p > __abc0⇔⇔吗?

    这些语句的等价性通常被假定,因为其他*=0操作符(即+=-=*=/=%=**=&=|=^=<<=-=0)也具有类似的等价性。然而,对于-=1,当-=2是对象上的方法而-=3是真值时,这些语句*=1的行为是不同的。在这种情况下,-=4将不做任何事情(除了求值到-=3),而-=6将在-=3的接收器上调用-=7。正如*=2所指出的,当调用-=9有副作用时,这可能会产生不同,例如向散列添加键。

  • < p > __abc0⇔⇔ ? ?

    这些语句的行为的不同之处在于当a为真值时,它们的值是什么。在这种情况下,a = b unless a将计算为nil(尽管a仍未设置,正如预期的那样),而a ||= b将计算为a

  • < p > __abc0⇔⇔ ? ?

    仍然没有。当存在返回a真值的method_missing方法时,这些语句可能会有所不同。在这种情况下,a ||= b将求值为method_missing返回的任何值,而不会尝试设置a,而defined?(a) ? (a || a = b) : (a = b)将设置ab,并求值为b

好,好,那么 a ||= b等价于什么?有办法在Ruby中表达这一点吗?

好吧,假设我没有忽略任何东西,我相信a ||= b在功能上等价于…(击鼓声)

begin
a = nil if false
a || a = b
end

坚持住!这不是第一个前面有noop的例子吗?嗯,不完全是。还记得我之前说过,只有当a是一个未定义的局部变量时,a ||= b才不等同于a || a = b吗?好吧,a = nil if false确保a永远不会是未定义的,即使这一行永远不会被执行。Ruby中的局部变量是词法范围的。

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

还请记住||=不是原子操作,因此,它不是线程安全的。根据经验,不要将它用于类方法。

< p > <代码>除非x X = y 结束代码< / > < / p >

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

等于

x ||= y

基本上, < p >
x ||= y表示

如果x有任何值,不要改变它的值,否则 set x to y

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

但这里有一个棘手的情况。如果a未定义,a || a = 42将引发NameError,而a ||= 42将返回42。所以,它们似乎不是等价的表达式。

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实例变量的值。

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

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

第一个例子:

x ||= 10

第二个例子:

x = 20
x ||= 10

在第一个例子中,x现在等于10。然而,在第二个例子中x已经定义为20。所以条件运算符没有作用。运行x ||= 10后,x仍然是20。

a ||= b等同于说a = b if a.nil?a = b unless a

但是所有3个选项都能显示相同的性能吗?在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,结果是类似的。

| | =是一个条件赋值运算符

  x ||= y

等于

  x = x || y

或者

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

A ||= b

表示如果'a'中存在任何值并且您不想更改它,则继续使用该值,否则如果'a'没有任何值,则使用'b'的值。

简单的话,如果左手边不为空,则指向现有的值,否则指向右边的值。

ruby-lang语法。正确的答案是查看ruby-lang文档。所有其他解释都令人困惑

谷歌

"ruby-lang文档缩写赋值"。

Ruby-lang文档

https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment

如果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