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
是未定义的或者是错误的 ( false
或nil
),则评估b
并将b
设置为结果 ”。
例如:
> a ||= 1; => 1 > a ||= 2; => 1 > foo = false; => false > foo ||= true; => true > foo ||= false; => true
Ruby的短路评估意味着,如果a
被定义并评估为真,那么操作员的右手边将不被评估,也不会进行任务分配。 如果a
和b
都是局部variables,则这种区别是不重要的,但是如果是类的getter / setter方法则是重要的。
令人困惑的是,它与其他赋值运算符(如+=
)类似,但行为不同。
a += b
→ a = a + b
a ||= b
→ a || a = b
a || a = b
有明显的细微差别,例外,特殊情况 – 但这是它的本质。
*sorting,但不完全
正如ajedi32指出的那样:
a ||= b
b⇔aa || a = b?
a || a = b?
当
a
是未定义的局部variables时,这些语句的行为有所不同。 在这种情况下,a ||= b
将a ||= b
设置为b
(并评估为b
),而a || a = b
a || a = b
会NameError: 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
表示:如果a
是undefined, 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
是未定义的或者虚假的( false
或nil
),将a
设置为b
并且评估为(即返回) b
,否则评估为”。
其他人经常试图通过说a ||= b
相当于a ||= b
来说明这一点 a || a = b
或a = a || b
a = a || b
。 这些等价性可以帮助理解这个概念,但要知道它们在所有情况下都是不准确的。 请允许我解释一下:
-
a ||= b
b⇔aa || a = b
a || a = b
?当
a
是未定义的局部variables时,这些语句的行为有所不同。 在这种情况下,a ||= b
将a ||= b
设置为b
(并评估为b
),而a || a = b
a || a = b
会NameError: undefined local variable or method 'a' for main:Object
引发NameError: undefined local variable or method 'a' for main:Object
。 -
a ||= b
b⇔aa = a || b
a = a || b
?这些陈述的等同性通常是假定的,因为对于其他简化的赋值操作符(即
+=
-=
,*=
/=
,%=
,**=
,&=
,|=
,^=
<<=
和>>=
)。 然而,对于||=
这些语句的行为可能会有所不同,当a=
对象的方法是一个真理。 在这种情况下,a ||= b
将不做任何事情(除了评估为a
),而a = a || b
a = a || b
将在接收器上打电话a=(a)
。 正如其他人所指出的,当调用a=a
时会产生不同的副作用,例如将键添加到散列。 -
a ||= b
b⇔aa = 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 = b
当a
是一个未定义的局部variables? 那么, a = nil if false
,则a = nil if false
确保a
永远不会未定义,即使该行从未被执行。 Ruby中的局部variables是词汇范围的。
unless x x = y end
除非x有一个值(不是nil或false),否则将其设置为y
相当于
x ||= y
假设a = 2
和b = 3
那么, a ||= b
将导致a
值,即2
。
因为当一个评估价值不会导致false
或nil
。这就是为什么它不会评估b
的价值。
现在假设a = nil
和b = 3
。
那么a ||= b
将导致3
即b
的值。
因为它首先尝试评估一个结果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
值将不会被分配给a
。 a
还会有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 ||= 10
后x
仍然是20。