Что означает || = (или-equals) в Ruby?

Что означает следующий код в Ruby?

||=

Есть ли у него какой-либо смысл или причина синтаксиса?

Ответ 1

Этот вопрос обсуждался так часто в списках рассылки Ruby и блогах Ruby, что теперь в списке рассылки Ruby есть даже темы, единственная цель которых - собирать ссылки на все остальные темы в списке рассылки Ruby, в которых обсуждается эта проблема.,

Здесь один: полный список || = (ИЛИ равно) тем и страниц

Если вы действительно хотите знать, что происходит, взгляните на Раздел 11.4.2.3 "Сокращенные назначения" проекта спецификации языка Ruby.

В первом приближении

a ||= b

эквивалентно

a || a = b

и не эквивалентно

a = a || b

Однако это только первое приближение, особенно если a не определено. Семантика также различается в зависимости от того, является ли это простым назначением переменной, назначением метода или назначением индексации:

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

все относятся по-разному.

Ответ 2

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 = b a || a = b. Разница в том, что когда a не определено, a || a = b a || a = b вызовет NameError, тогда как a ||= b устанавливает a в b. Это различие неважно, если a и b являются локальными переменными, но важно, если какой-либо из них является методом получения/установки класса.

Дальнейшее чтение:

Ответ 3

Краткий и полный ответ

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 в комментариях, это имеет значение true, если: 1. a - определенная переменная. 2. Оценка одного и двух раз не приводит к разнице в программном или системном состоянии.

Ответ 4

Короче говоря, a||=b означает: Если a - undefined, nil or false, назначьте b в a. В противном случае сохраните a неповрежденным.

Ответ 5

В принципе,


x ||= y означает

если x имеет какое-либо значение, оставьте его в покое и не изменяйте значение, в противном случае установите x на y

Ответ 6

Это означает или равно. Он проверяет, определено ли значение слева, затем используйте это. Если это не так, используйте значение справа. Вы можете использовать его в Rails для кэширования переменных экземпляра в моделях.

Быстрый пример на основе Rails, где мы создаем функцию для извлечения текущего зарегистрированного пользователя:

class User > ActiveRecord::Base

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

end

Он проверяет, установлена ​​ли переменная экземпляра @current_user. Если это так, он вернет его, тем самым сохранив вызов базы данных. Если он не установлен, мы делаем вызов, а затем устанавливаем переменную @current_user. Это действительно простой метод кэширования, но он отлично подходит для тех случаев, когда вы несколько раз извлекаете одну и ту же переменную экземпляра в приложении.

Ответ 7

x ||= y

является

x || x = y

", если x является ложным или undefined, то x указывает на y"

Ответ 8

Чтобы быть точным, a ||= b означает "если a - undefined или ложь (false или nil), установите a в b и оцените (т.е. верните) b, иначе оцените a".

Другие часто пытаются проиллюстрировать это, сказав, что a ||= b эквивалентно a || a = b или a = a || b. Эти эквивалентности могут быть полезны для понимания концепции, но имейте в виду, что они не точны при любых условиях. Позвольте мне объяснить:

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

    Поведение этих операторов отличается, когда a является локальной переменной undefined. В этом случае a ||= b установит a в b (и оценит до b), тогда как a || a = b поднимет NameError: undefined local variable or method 'a' for main:Object.

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

    Эквивалентность этих утверждений часто принимается, поскольку аналогичная эквивалентность верна для других сокращенного присваивания операторов (т.е. +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<= и >>=). Однако для ||= поведение этих операторов может отличаться, если a= является методом объекта, а a является правдивым. В этом случае a ||= b ничего не сделает (кроме оценки a), тогда как a = a || b вызовет a=(a) на приемнике a. Как отметили другие, это может иметь значение при вызове a=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) установит a в b и оценит до b.

Хорошо, хорошо, так что эквивалентно a ||= b? Есть ли способ выразить это в Ruby?

Ну, полагая, что я ни о чем не забываю, я считаю, что a ||= b функционально эквивалентен... (drumroll)

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

Держись! Разве это не первый пример с noop перед этим? Ну, не совсем. Помните, как я сказал ранее, что a ||= b не эквивалентен a || a = b, когда a является локальной переменной undefined? Ну, a = nil if false гарантирует, что a никогда не undefined, хотя эта строка никогда не выполняется. Локальные переменные в Ruby лексически ограничены.

Ответ 9

Предположим a = 2 и b = 3

THEN, a ||= b будет приведено к значению a i.e. 2.

Как и когда a оценивает до некоторого значения, не приведенного к false или nil.. Поэтому он ll не оценивает значение b.

Теперь предположим a = nil и b = 3.

Затем a ||= b будет приведено к значению 3 i.e 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

Где, User.find_by_id(session[:user_id]) запускается тогда и только тогда, когда @current_user не инициализируется раньше.

Ответ 10

unless x x = y end

если значение x не имеет значения (оно не равно nil или false), установите его равным y

эквивалентно

x ||= y

Ответ 11

a ||= b

эквивалентно

a || a = b

а не

a = a || b

из-за ситуации, когда вы определяете хэш с по умолчанию (хэш возвращает значение по умолчанию для любых undefined)

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

если вы используете:

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

a все еще:

{}

но когда вы пишете его так:

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

a становится:

{10 => true}

потому что вы назначили значение самого себя в ключе 10, которое по умолчанию имеет значение true, поэтому теперь хэш определен для ключа 10, а не для выполнения задания в первую очередь.

Ответ 12

Это как ленивый экземпляр. Если переменная уже определена, она будет принимать это значение вместо создания значения снова.

Ответ 13

Это обозначение назначения по умолчанию

например: x || = 1
это проверит, чтобы видеть, является ли x нулем или нет. Если x действительно равен nil, ему будет присвоено это новое значение (1 в нашем примере)

более явно:
если х == ноль
х = 1
конец

Ответ 14

Также помните, что ||= не является атомной операцией, и поэтому он не является потокобезопасным. Как правило, не используйте его для методов класса.

Ответ 15

Как общее заблуждение, a ||= b не эквивалентно a = a || b a = a || b, но он ведет себя как a || a = b a || a = b.

Но тут возникает сложный случай. Если a не определено, a || a = 42 a || a = 42 вызывает NameError, а a ||= 42 возвращает 42. Таким образом, они не кажутся эквивалентными выражениями.

Ответ 16

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

Потому что a был nil

Ответ 17

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.

Его шаблон Memoization, который используется в Ruby для ускорения доступа к ним.

def users
  @users ||= User.all
end

В основном это означает:

@users = @users || User.all

Таким образом, вы впервые позвоните в базу данных, когда вы вызовете этот метод.

Будущие вызовы этого метода просто вернут значение переменной @users экземпляра.

Ответ 18

|| = - оператор условного присваивания

  x ||= y

эквивалентно

  x = x || y

или альтернативно

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

Ответ 19

a || = b

Указывает, присутствует ли какое-либо значение в "a", и вы не хотите изменять его, продолжая использовать это значение, иначе, если "a" не имеет никакого значения, используйте значение "b".

Простые слова, если слева, если не ноль, указывают на существующее значение, в противном случае указывают на значение справа.

Ответ 20

||= присваивает значение правому значению, только если left == nil (или не определено, либо false).

Ответ 21

||= называется оператором условного присваивания.

В основном он работает как =, но за исключением того, что если переменная уже назначена, она ничего не сделает.

Первый пример:

x ||= 10

Второй пример:

x = 20
x ||= 10

В первом примере x теперь равно 10. Однако во втором примере x уже определено как 20. Таким образом, условный оператор не действует. x по-прежнему 20 после запуска x ||= 10.

Ответ 22

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 перед каждым заданием.