Ruby中的|| =(或 – 等于)是什么意思?

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

||= 

它有什么意义或语法的理由?

这个问题在Ruby邮件列表和Ruby博客上经常被讨论,现在在Ruby邮件列表上甚至有线程,其唯一目的是收集链接到Ruby邮件列表上的所有其他线程 ,讨论这个问题。

下面是一个: || =(或等于)线程和页面的权威列表

如果您真的想知道发生了什么,请参阅Ruby语言草稿规范的第11.3.1.2节“缩写赋值”。

作为第一个近似,

 a ||= b 

相当于

 a || a = b 

并不等同于

 a = a || b 

但是,这只是第一个近似值,特别是如果a是未定义的。 语义也是不同的,取决于它是简单的variables赋值,方法赋值还是索引赋值:

 a ||= b ac ||= b a[c] ||= b 

都被区别对待。

a ||= b是一个“条件赋值运算符”。 对于a || a = b这是一种不太完整的 (*)速记 a || a = b

这意味着“ 如果a是未定义的或者是错误的 ( falsenil ),则评估b并将b设置为结果 ”。

例如:

 > a ||= 1; => 1 > a ||= 2; => 1 > foo = false; => false > foo ||= true; => true > foo ||= false; => true 

Ruby的短路评估意味着,如果a被定义并评估为真,那么操作员的右手边将不被评估,也不会进行任务分配。 如果ab都是局部variables,则这种区别是不重要的,但是如果是类的getter / setter方法则是重要的。

令人困惑的是,它与其他赋值运算符(如+= )类似,但行为不同。

a += ba = a + b

a ||= ba || a = b a || a = b

有明显的细微差别,例外,特殊情况 – 但这是它的本质。

*sorting,但不完全

正如ajedi32指出的那样:

a ||= b b⇔a a || a = b? a || a = b?

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

进一步阅读:

简洁而完整的答案

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

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

基本上,

x ||= y表示

如果x有任何值,则不要改变值,否则将x设置为y

 x ||= y 

 x || x = y 

“如果x是假的或未定义的,那么x指向y”

这意味着或等于。 它会检查左侧的值是否已定义,然后使用该值。 如果不是,请使用右侧的值。 您可以在Rails中使用它来caching模型中的实例variables。

一个基于Rails的快速示例,我们创build一个函数来获取当前login的用户:

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

它检查是否设置了@current_user实例variables。 如果是,它将返回它,从而保存数据库调用。 如果没有设置,我们进行调用,然后设置@current_uservariables。 这是一个非常简单的caching技术,但是当您多次在整个应用程序中获取相同的实例variables时非常有用。

确切地说, a ||= b意思是“如果a是未定义的或者虚假的( falsenil ),将a设置为b并且评估为(即返回) b ,否则评估为”。

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

  • a ||= b b⇔a a || a = b a || a = b

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

  • a ||= b b⇔a a = a || b a = a || b

    这些陈述的等同性通常是假定的,因为对于其他简化的赋值操作符(即+= -=*= /=%=**=&=|=^= <<=>>= )。 然而,对于||=这些语句的行为可能会有所不同,当a=对象的方法是一个真理。 在这种情况下, a ||= b将不做任何事情(除了评估为a ),而a = a || b a = a || b将在接收器上打电话a=(a) 。 正如其他人所指出的,当调用a=a时会产生不同的副作用,例如将键添加到散列。

  • a ||= b b⇔a a = b unless a ??

    这些陈述的行为只有在他们评价为真的时才会有所不同。 在这种情况下, a = b unless a将评估nil (但a仍然不会如预期的那样设置),而a ||= b则评估为a

  • a ||= b b⇔ defined?(a) ? (a || a = b) : (a = b) defined?(a) ? (a || a = b) : (a = b)

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

好的,好的,那么a ||= b等于什么? 有没有办法在Ruby中expression这个?

那么,假设我没有忽略任何东西,我相信a ||= b在function上等同于…( drumroll

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

等一下! 这不就是之前noop的第一个例子吗? 那么,不完全。 请记住,我之前说过, a ||= b只是不等于a || a = b a || a = ba是一个未定义的局部variables? 那么, a = nil if false ,则a = nil if false确保a永远不会未定义,即使该行从未被执行。 Ruby中的局部variables是词汇范围的。

unless x x = y end

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

相当于

x ||= y

假设a = 2b = 3

那么, a ||= b将导致a值,即2

因为当一个评估价值不会导致falsenil 。这就是为什么它不会评估b的价值。

现在假设a = nilb = 3

那么a ||= b将导致3b的值。

因为它首先尝试评估一个结果nil的价值,所以它评估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 

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

 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} 

因为你已经在关键字10处分配了自己的值,默认值为true,所以现在散列是为关键字10定义的,而不是从不首先执行分配。

这就像懒惰的实例。 如果variables已经被定义了,它将会取这个值,而不是再次创build值。

这是默认的赋值符号

例如:x || = 1
这将检查x是否为零。 如果x确实为零,则它会将其赋值为新值(在本例中为1)

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

 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 

因为是nil

 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中使用的Memoization模式来加速访问器。

 def users @users ||= User.all end 

这基本上转化为:

 @users = @users || User.all 

所以你第一次调用这个方法时会打电话给数据库。

将来调用这个方法只会返回@users实例variables的值。

请记住||=不是一个primefaces操作,所以它不是线程安全的。 根据经验,不要将它用于类方法。

作为一个常见的误解,一个|| = b不等于a = a || b,但它是行为像一个|| a = b

但是这里有一个棘手的案例

如果a没有定义,则|| a = 42引发NameError,而一个|| = 42则返回42.因此,它们似乎不是等价的expression式。

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

它的基本原理是=但是如果一个variables已经被赋值,它将不会执行任何操作。

第一个例子:

 x ||= 10 

第二个例子:

 x = 20 x ||= 10 

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