ChatGPT解决这个技术问题 Extra ChatGPT

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

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

||=

它对语法有任何意义或理由吗?


S
Steve Bennett

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 ||= ba 设置为 b。如果 ab 都是局部变量,则这种区别并不重要,但如果其中任何一个是类的 getter/setter 方法,这种区别就很重要。

进一步阅读:

http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html


没有进行足够的搜索,但仍然不明白为什么要使用它而不是 a = a ||湾。也许只是我个人的看法,但存在这样的细微差别有点荒谬......
@dtc,考虑 h = Hash.new(0); h[1] ||= 2。现在考虑两种可能的扩展 h[1] = h[1] || 2h[1] || h[1] = 2。两个表达式的计算结果都是 0,但第一个不必要地增加了散列的大小。也许这就是 Matz 选择让 ||= 表现得更像第二个扩展的原因。 (我基于另一个答案中链接的一个线程的示例。)
我喜欢另一个关于它有多深入的答案,但我喜欢这个答案的简单性。对于学习 Ruby 的人来说,这是我们需要的答案类型。如果我们知道 ||= 的含义,那么问题的措辞可能会有所不同。
仅供参考,如果 a 未定义,a || a = b 会引发 NameErrora ||= b 没有,而是初始化 a 并将其设置为 b。据我所知,这是两者之间的唯一区别。同样,我知道的 a = a || ba ||= b 之间的唯一区别是,如果 a= 是一个方法,那么无论 a 返回什么,它都会被调用。此外,我知道的 a = b unless aa ||= b 之间的唯一区别是,如果 a 为真,则该语句的计算结果为 nil 而不是 a。很多近似值,但没有什么完全等价的......
请查看问题下的评论
r
robosnacks

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

这是一个:The definitive list of ||= (OR Equal) threads and pages

如果您真的想知道发生了什么,请查看 Ruby Language Draft Specification 的第 11.4.2.3 节“Abbreviated assignments”。

作为第一个近似值,

a ||= b

相当于

a || a = b

并且不等于

a = a || b

但是,这只是第一个近似值,尤其是在 a 未定义的情况下。语义也不同,具体取决于它是简单的变量赋值、方法赋值还是索引赋值:

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

都被区别对待。


这是一个非常神秘的非答案。简短的回答似乎是:a ||= b 表示,如果 a 未定义,则为其分配 b 的值,否则不理会它。 (好吧,有细微差别和特殊情况,但这是基本情况。)
@SteveBennett:我不会称 a = false; a ||= true 确实按照您的回答所说的那样做“细微差别”。
也许这个问题已经被问过很多次了,因为人们一直在回答这个问题已经被问过很多次了。
有了这个答案,很容易看出为什么会有多个线程。如果您尝试使用新手帽子搜索此问题的答案,您会发现所有答案都不清楚。例如,对于这个,你只是在说什么不是。我建议改进你的答案,给新手一个简单的答案:a = b 除非 a
根本不是一个好的答案。不知道为什么这被接受了。它几乎没有试图解释 ||= 是什么,而是试图将某人指向另一个线程(这很讽刺,因为你试图结束这个追逐)。为什么不直接说它是什么?我相信它会为你和读者节省更多的工作。否决。
f
frediy

简洁而完整的答案

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 为假/零/未定义,则会对其进行两次评估。 (但我不了解 Ruby,所以我不知道 lvalues 是否可以准确地“评估”......)
我明白你在说什么。我所说的两行等价的意思是,在评估整行之后,结束状态将是等价的,这意味着 a、b 的值以及返回的内容。 ruby 解释器是否使用不同的状态——比如对 a 的多次评估——完全有可能实现。那里有任何 ruby 解释器专家吗?
这不太对。如果 a 未定义,a || a = ba ? a : a = bif a then a else a = b endif a then a = a else a = b end 将引发错误,而 a ||= ba = a || b 不会。此外,当 a 为真时,a || a = ba ? a : a = bif a then a else a = b enda = a ? a : bif a then a = a else a = b enda 求值两次,而 a ||= ba = a || b 则不然。
*更正:当 a 为真时,a || a = b 不会计算 a 两次。
@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。
v
vidang

简而言之,a||=b 的意思是:如果 aundefined, nil or false,则将 b 分配给 a。否则,保持 a 不变。


n
nPcomp


x ||= y 表示

如果 x 有任何值,不要理会它,不要更改值,否则将 x 设置为 y


不对;如果 x 的值是假的(nilfalse),x 仍然可以有一个值并且仍然可以进行分配。
J
Jamie Rumbelow

它的意思是或等于。它检查左侧的值是否已定义,然后使用它。如果不是,请使用右侧的值。您可以在 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-Forum.Com/topic/151660 和其中提供的链接。
@Jo(umlaut)rg,我看不出它有什么问题。您的链接是其他链接的列表。没有真正的解释为什么它是错误的,只是听起来像是对你的价值判断。
这个答案是错误的,因为它不仅在 undefined 上触发,而且在 falsenil 上触发,这可能与 current_user 无关,但尤其是 false 在其他情况下可能会出乎意料
尽管此答案可能表现出任何不完整(不适用于 nil/false),但它首先解释了您为什么要使用 ||=,所以谢谢!
K
Kiattisak Anoochitarom
x ||= y

x || x = y

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


C
Community

准确地说,a ||= b 表示“如果 a 未定义或为假(falsenil),则将 a 设置为 b 并计算为(即返回)b,否则计算为 { 2}”。

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

一个 ||= b ⇔ 一个 || 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 ||乙?通常假设这些语句的等价性,因为对于其他缩写赋值运算符(即 +=、-=、*=、/=、%=、**=、&=、|=、^=、 <<= 和 >>=)。但是,对于 ||=,当 a= 是对象上的方法且 a 为真时,这些语句的行为可能会有所不同。在这种情况下,a ||= b 将什么都不做(除了评估为 a),而 a = a || b 将在 a 的接收器上调用 a=(a)。正如其他人指出的那样,当调用 a=a 有副作用时,这可能会有所不同,例如将键添加到哈希中。

||= b ⇔ a = b 除非 a??当 a 为真时,这些语句的行为仅在它们评估的内容上有所不同。在这种情况下,a = b 除非 a 将评估为 nil(尽管 a 仍不会像预期的那样被设置),而 a ||= b 将评估为 a。

a ||= b ⇔ 已定义?(a) ? (a || a = b) : (a = b)????仍然没有。当存在为 a 返回真值的 method_missing 方法时,这些语句可能会有所不同。在这种情况下, a ||= b 将评估任何method_missing返回,而不是尝试设置a,而定义?(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 是未定义的局部变量时,a ||= b 才不等同于 a || a = b?好吧,a = nil if false 确保 a 永远不会未定义,即使该行从未执行过。 Ruby 中的局部变量是词法范围的。


所以你扩展的第三个例子:(a=b unless a) or a
@vol7ron 这与#2有类似的问题。如果 a 是一个方法,它将被调用两次而不是一次(如果它第一次返回一个真值)。例如,如果 a 需要很长时间才能返回或有副作用,这可能会导致行为不同。
另外,第一句话,不应该说 assign b to a,rhs 是否仍然分配给 lhs,或者换句话说,lhs 是否仍然设置它对 rhs 的价值?
z
z atef

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

0
0r4cl3

unless x x = y end

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

相当于

x ||= y


A
Alex Weitz

假设 a = 2b = 3

那么,a ||= b 将得到 a 的值,即 2

就像当 a 评估某个值时不会导致 falsenil.. 这就是它 ll 不评估 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])


S
Sunda

||= 是条件赋值运算符

  x ||= y

相当于

  x = x || y

或者

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

S
SHUBHAM SHARMA

一个 ||= b

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

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


R
RileyE
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 定义了哈希,而不是从一开始就不执行分配。


m
mukh007

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


L
Luca Guidi

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


F
Felix

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

它基本上与 = 一样工作,但如果变量已被分配,它将什么也不做。

第一个例子:

x ||= 10

第二个例子:

x = 20
x ||= 10

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


M
Max Rogers

这是默认的赋值符号

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

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


nilfalse,不仅是 nil
S
Scott Weldon
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 实例变量的值。


s
sawa

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

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


f
fulvio
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


m
meh

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

谷歌

“ruby-lang 文档缩写作业”。

Ruby 语言文档

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


Y
Ymox

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