Что означает ||= (или-равно) в 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. Все остальные объяснения запутываются.
"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
ноль перед каждым назначением.