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

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

||=

Имеет ли это какое-либо значение или причину для синтаксиса?

20 ответов

Решение

Этот вопрос обсуждался так часто в списках рассылки 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

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

a ||= b является "оператором условного присваивания". Это что- то вроде, но не совсем (*) сокращение для a || a = b,

Это значит " если a не определено или ложно ( false или же nil ), тогда оцените b и установить a к результату ".

Например:

> a ||= nil
=> nil
> a ||= 0;
=> 0
> a ||= 2;
=> 0

> foo = false;
=> false
> foo ||= true;
=> true
> foo ||= false;
=> true

Оценка короткого замыкания Руби означает, что если a определяется и оценивается как истинный, тогда правая часть оператора не оценивается, и присваивание не выполняется. Это различие неважно, если a а также b обе являются локальными переменными, но имеют значение, если любой из них является методом получения / установки класса.

Сбивает с толку, он похож на другие операторы присваивания (такие как +=) но ведет себя по-разному.

a += b переводит на a = a + b

a ||= b примерно переводится как * a || a = b

* Кроме того, когда a не определено, a || a = b будет NameError, тогда как a ||= b наборы a в b,

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

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

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||=b означает: если a является undefined, nil or falseназначить b в a, В противном случае продолжайте a неповрежденными.

В принципе,


x ||= y средства

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

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

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

class User > ActiveRecord::Base

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

end

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

Если 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

Точнее, a ||= b означает "если a не определено или ложно (false или же nil), задавать a в b и оценить (т.е. вернуть) b иначе оцените до a ".

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

  • a ||= b a || 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 || b?

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

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

    Поведение этих утверждений отличается только тем, что они оценивают, когда a это правда В таком случае, a = b unless a будет оценивать nil (хоть a все равно не будет установлен, как ожидалось), тогда как a ||= b будет оценивать a,

  • a ||= b defined?(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 функционально эквивалентно... (барабанная дробь)

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

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

x ||= y

является

x || x = y

"если x ложно или не определено, то x указывает на y"

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

  x ||= y

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

  x = x || y

или в качестве альтернативы

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

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

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

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

x ||= 10

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

x = 20
x ||= 10

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

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

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

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

a ||= b

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

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

Предполагать a = 2 а также b = 3

ЗАТЕМ, a ||= b будет приведено к aценность то есть 2,

Как, например, когда оценивается какое-то значение, не приведенное к false или же nil.. Вот почему это ll не оценивать 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

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

unless x x = y end

если х не имеет значения (это не ноль или ложь), установите его равным у

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

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[10] = a[10] || 10

становится:

{10 => true}

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

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

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

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

Но тут возникает сложный случай

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

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

Этот синтаксис ruby-lang. Правильный ответ - проверить документацию по ruby-lang. Все остальные объяснения запутываются.

Google

"ruby-lang docs Abbreviated Assignment".

Документы на Ruby-lang

https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html

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 для ускорения доступа.

def users
  @users ||= User.all
end

Это в основном означает:

@users = @users || User.all

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

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

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

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

Другие вопросы по тегам