Что означает следующий код в Ruby?
||=
Имеет ли это какое-либо значение или причину для синтаксиса?
Что означает следующий код в Ruby?
||=
Имеет ли это какое-либо значение или причину для синтаксиса?
Ответы:
Этот вопрос обсуждался так часто в списках рассылки 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 = false; a ||= true
вовсе не делать то , что ваш ответ говорит , что это делает «нюанс».
a ||= b
является условным оператором присваивания . Это означает, что если значение a
undefined или false , то оцените 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 ||= b
устанавливается a
на b
. Это различие неважно, если a
и b
являются локальными переменными, и имеет значение, если любой из них является методом получения / установки класса.
Дальнейшее чтение:
h = Hash.new(0); h[1] ||= 2
. Теперь рассмотрим два возможных расширения h[1] = h[1] || 2
против h[1] || h[1] = 2
. Оба выражения имеют значение, 0
но первое излишне увеличивает размер хэша. Возможно, именно поэтому Матц решил сделать ||=
поведение более похожим на второе расширение. (Я основал это на примере одной из тем, связанных с другим ответом.)
a || a = b
поднимает, NameError
если a
неопределено. a ||= b
нет, но вместо этого инициализирует a
и устанавливает его b
. Это единственное различие между этими двумя, насколько я знаю. Точно так же единственное различие между a = a || b
и тем, a ||= b
что я знаю, заключается в том, что если a=
это метод, он будет вызываться независимо от того, что a
возвращает. Кроме того , единственное различие между a = b unless a
и a ||= b
что я знаю, что это заявление имеет значение , nil
а не a
если a
это truthy. Множество приближений, но ничего совершенно не эквивалентного ...
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
false / zero / undefined, он оценивается дважды. (Но я не знаю Руби, поэтому я не знаю, можно ли точно оценить lvalues ...)
a || a = b
, a ? a : a = b
, if a then a else a = b end
, И if a then a = a else a = b end
выдаст ошибку , если a
не определено, тогда a ||= b
и a = a || b
не будет. Кроме того , a || a = b
, a ? a : a = b
, if a then a else a = b end
, a = a ? a : b
, и if a then a = a else a = b end
оценить в a
два раза , когда a
это truthy, в то время как a ||= b
и a = a || b
нет.
a || a = b
не будет оцениваться a
дважды, когда a
это правда.
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 = 5
, self.a ||= b
вернется 6, но self.a ? self.a : self.a = b
вернется 7.
Это означает или равно. Он проверяет, определено ли значение слева, а затем используйте его. Если это не так, используйте значение справа. Вы можете использовать его в Rails для кэширования переменных экземпляра в моделях.
Быстрый пример на основе Rails, где мы создаем функцию для извлечения текущего пользователя:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
Он проверяет, установлена ли переменная экземпляра @current_user. Если это так, он вернет его, тем самым сохраняя вызов базы данных. Однако, если он не установлен, мы делаем вызов, а затем устанавливаем переменную @current_user. Это действительно простая техника кэширования, но она отлично подходит для случаев, когда вы выбираете одну и ту же переменную экземпляра в приложении несколько раз.
undefined
, но и включается false
и nil
, что может не иметь отношения к делу current_user
, но особенно false
может быть непредсказуемым в других случаях
x ||= y
является
x || x = y
«если x ложно или не определено, то x указывает на y»
Чтобы быть точным, a ||= b
означает «если a
не определено или falsy ( 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
приемник «s. Как уже отмечали другие , это может иметь значение, когда у вызова 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 имеют лексическую область видимости.
(a=b unless a) or a
a
это метод, он будет вызываться дважды, а не один раз (если он возвращает истинное значение в первый раз). Это может привести к отличиям в поведении, если, например, a
возвращение занимает много времени или имеет побочные эффекты.
b
кa
, не в шк еще назначить на LHS, или другими словами, не в л.ш. до сих пор установить его значение в правой части?
a ||= b
ответ, который я нашел в Интернете. Спасибо.
Предположим a = 2
иb = 3
ТОГДА, a ||= b
будет приведено к a
«S значение , т.е. 2
.
Как, например, когда a оценивает какое-то значение, не приведенное к false
или nil
.. Поэтому оно ll
не оценивает b
значение.
Теперь предположим a = nil
и b = 3
.
Тогда a ||= b
будет приведено значение 3
ie b
.
Когда он сначала попытается оценить значение a, которое привело к 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
не инициализирован ранее.
a || = b
Указывает, присутствует ли какое-либо значение в «a», и вы не хотите изменять его, продолжая использовать это значение, иначе, если «a» не имеет никакого значения, используйте значение «b».
Простые слова, если слева, если не ноль, указывают на существующее значение, в противном случае указывают на значение справа.
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
, а не выполнять его в первую очередь.
Пожалуйста, помните, что ||=
это не атомарная операция и поэтому она не является поточно-ориентированной. Как правило, не используйте его для методов класса.
Это обозначение назначения по умолчанию
например: x || = 1
это проверит, чтобы видеть, является ли x нулем или нет. Если x действительно равен nil, ему будет присвоено это новое значение (1 в нашем примере)
более явно:
если x == nil
x = 1
end
nil
или false
не толькоnil
Если значение 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
||=
присваивает значение вправо, только если left == nil (или не определено или false).
Это синтаксис рубинового языка. Правильный ответ - проверить документацию ruby-lang. Все остальные объяснения запутывают .
"ruby-lang docs Сокращенное Назначение".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
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
переменной экземпляра.
||=
называется оператором условного присваивания.
Это в основном работает как, =
но за исключением того, что, если переменная уже была назначена, она ничего не будет делать.
Первый пример:
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
ноль перед каждым заданием.