Что означает ||= (или-equals) в 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
неопределено или falsey (false
или nil
), затем оценки b
и set a
в результате".
например:
> a ||= nil
=> nil
> a ||= 0;
=> 0
> a ||= 2;
=> 0
> foo = false;
=> false
> foo ||= true;
=> true
> foo ||= false;
=> true
оценка короткого замыкания Ruby означает, что если a
определено и оценивает к truthy, тогда правая сторона оператор не оценивается, и назначение не выполняется. Это различие не имеет значения, если a
и b
как локальные переменные, но значительна, если это геттер/сеттер класса.
смущающе, он похож на другие операторы присваивания (например,+=
), но ведет себя по-разному.
a += b
переводится как a = a + b
a ||= b
грубо говоря* a || a = b
*кроме того, когда a
неопределено, a || a = b
было бы NameError, тогда как a ||= b
наборы a
to 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
-
Edit: как указал AJedi32 в комментариях, это справедливо только в том случае, если: 1. a-определенная переменная. 2. Оценив один раз и два раза не результат-разница в состоянии программы или системы.
короче, a||=b
означает: если a
is undefined, nil or false
, назначить b
to a
. В противном случае, держите a
нетронутыми.
В основном,
x ||= y
означает
если x
имеет любое значение оставьте его в покое и не изменяйте значение, иначе
set x
to 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 в это. Это очень простой метод кэширования, но отлично подходит для того, когда вы извлекаете одну и ту же переменную экземпляра через приложение несколько раз.
если быть точным,a ||= b
означает: "если a
не определено или ложно (false
или nil
), set a
to b
и оценить (т. е. возвращение) b
, в противном случае оцените до a
".
другие часто пытаются проиллюстрировать это тем, что a ||= b
эквивалентно a || a = b
или a = a || b
. Эти эквивалентности могут быть полезны для понимания концепции, но имейте в виду, что они не точный при любых условиях. Позвольте мне объясните:
-
a ||= b
⇔a || a = b
?поведение этих утверждений отличается, когда
a
является неопределенной локальной переменной. В таком случае ...--1--> устанавливаетсяa
tob
(и оценки кb
), аa || a = b
поднятьNameError: undefined local variable or method 'a' for main:Object
. -
a ||= b
⇔a = a || b
?эквивалентность этих утверждений часто предполагается, так как аналогичная эквивалентность верно для других сокращенное задание операторов (т. е.
+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
и>>=
). Однако для||=
поведение этих заявлений мая по-разномуa=
является методом на объекте иa
- истина. В таком случае ...--1--> ничего не будет делать (кроме оценки дляa
), аa = a || b
будем называтьa=(a)
ona
'приемник С. Как другие указали, что это может иметь значение при вызовеa=a
имеет побочные эффекты, такие как добавление ключей в хэш. -
a ||= b
⇔a = b unless a
??поведение этих утверждений отличается только тем, что они оценивают, когда
a
- истина. В таком случае ...--44--> будет оценено какnil
(хотяa
все равно не будет установлен, как ожидалось), тогда какa ||= b
будет оцененоa
. -
a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????все еще нет. Эти утверждения могут отличаться, когда a
method_missing
существует метод, который возвращает истинное значение дляa
. В этом случаеa ||= b
будет оценить всеmethod_missing
возвращает и не пытается установитьa
, тогда какdefined?(a) ? (a || a = b) : (a = b)
устанавливаетсяa
tob
и оценкиb
.
Ладно, ладно, так что is a ||= b
эквивалент чтобы? Есть ли способ выразить это в Ruby?
ну, предполагая, что я ничего не упускаю, я верю a ||= b
аналогично... (барабанная дробь)
begin
a = nil if false
a || a = b
end
держись! Разве это не первый пример с noop перед ним? Ну, не совсем. Помните, как я говорил до этого?--1--> только не эквивалентно a || a = b
, когда a
является неопределенной локальной переменной? Ну,a = nil if false
обеспечивает a
никогда не является неопределенным, хотя эта строка никогда не выполняется. Локальные переменные в Ruby лексически ограничены.
unless x
x = y
end
Если x не имеет значения (это не nil или false), установите его равным y
эквивалентно
x ||= y
предположим a = 2
и b = 3
затем, a ||= b
будет приведено к a
значение т. е. 2
.
как когда A оценивает некоторое значение, не приведенное к false
или nil
.. Вот почему это ll
не оценки b
's значение.
Теперь Предположим, Что a = nil
и b = 3
.
затем a ||= b
будет приведено к 3
то есть b
's значение.
как он сначала пытается оценить значение a, которое привело к nil
.. поэтому он оценил b
's значение.
лучший пример, используемый в приложении 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
не инициализируется раньше.
это обозначение назначения по умолчанию
например: x ||= 1
это проверит, является ли x нулевым или нет. Если x действительно равен нулю, он назначит ему это новое значение (1 в нашем примере)
более явный:
если x == nil
x = 1
конец
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
a становится:
{10 => true}
потому что вы присвоили себе значение по клавише 10
, который по умолчанию равен true, поэтому теперь хэш определен для ключа 10
, вместо того, чтобы никогда не выполнять задание в первую очередь.
Это как ленивый экземпляр. Если переменная уже определена, она будет принимать это значение вместо создания значения снова.
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
помните, что ||=
не является атомной операцией, и поэтому она не является потокобезопасной. Как правило, не используйте его для методов класса.
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
переменной экземпляра.
Как распространенное заблуждение a||=b не эквивалентно a = a || b, но это так, но он ведет себя как a / / a = B
но вот идет хитрый случай
Если a не определено, a | | a = 42 вызывает NameError, а a / / = 42 возвращает 42. Таким образом, они не кажутся эквивалентными выражениями.
||=
называется оператором условного присваивания.
он в основном работает как =
но за исключением того, что если переменная уже назначен он ничего не сделает.
первый пример:
x ||= 10
второй пример:
x = 20
x ||= 10
в первом примере x
теперь равно 10. Однако, во втором примере x
уже определено как 20. Таким образом, условный оператор не имеет эффекта. x
еще 20 после запуска x ||= 10
.
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
ноль перед каждым заданием.
|/= это условный оператор присваивания
x ||= y
эквивалентно
x = x || y
или
if defined?(x) and x
x = x
else
x = y
end