Відповіді:
Це питання настільки часто обговорювалося в розсилках Ruby-розсилках та блогах Ruby, що зараз є навіть потоки в списку розсилки Ruby, єдиною метою яких є збирання посилань на всі інші потоки в списку розсилки Ruby, які обговорюють цю проблему. .
Ось один: остаточний список || = (АБО рівних) тем та сторінок
Якщо ви дійсно хочете знати, що відбувається, перегляньте розділ 11.4.2.3 "Скорочені завдання" проекту технічної мови Ruby Language .
Як перше наближення,
a ||= b
еквівалентно
a || a = b
і не еквівалентно
a = a || b
Однак це лише перше наближення, особливо якщо a
воно не визначене. Семантика також відрізняється залежно від того, чи це просте призначення змінної, призначення методу або призначення індексації:
a ||= b
a.c ||= b
a[c] ||= b
всі трактуються по-різному.
a = false; a ||= true
зовсім НЕ робити те , що ваша відповідь говорить , що це робить «нюанс».
a ||= b
є оператором умовного призначення . Це означає, що якщо a
невизначено чи фальсифіковано , то оцініть b
і встановіть a
результат . Еквівалентно, якщо a
це визначено та оцінюється на truthy, то 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
обидві локальні змінні, але є суттєвою, якщо будь-який метод getter / setter класу.
Подальше читання:
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
його приймача. Як зазначали інші , це може змінити ситуацію, коли виклик 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
існує метод, який повертає значення "truthy" 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
Зачекай! Хіба це не лише перший приклад, коли перед ним лежить ніоп? Ну, не зовсім. Пам'ятайте, як я говорив раніше, 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
', тобто 2
.
Як коли коли оцінюється якесь значення, яке не призвело до false
або nil
.. Тому воно ll
не оцінює b
значення.
Тепер припустимо a = nil
і b = 3
.
Тоді a ||= b
буде виведено значення 3
ie 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
раніше не ініціалізовано.
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 все ще:
{}
але коли ти пишеш це так:
a[10] = a[10] || 10
a стає:
{10 => true}
тому що ви присвоїли значення себе за ключем 10
, яке за замовчуванням відповідає істині, тепер хеш визначається для ключа 10
, а не ніколи не виконує призначення в першу чергу.
Також пам’ятайте, що ||=
це не атомна операція, і це не безпечно для потоків. Як правило, не використовуйте його для методів класу.
Це нотація про призначення за замовчуванням
наприклад: x || = 1
це перевірить, чи є х нульовим чи ні. Якщо x дійсно дорівнює нулю, він присвоїть йому нове значення (1 у нашому прикладі)
більш чітко:
якщо x == nil
x = 1
кінець
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
||=
присвоює значення праворуч лише у тому випадку, якщо ліворуч == нуль (або невизначено або невірно).
Цей синтаксис "ruby-lang". Правильна відповідь - перевірити документацію на ruby-lang. Всі інші пояснення пригнічують .
"Скорочене призначення" ruby-lang docs ".
https://docs.ruby-lang.org/uk/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
нулі перед кожним завданням.