Відповіді:
Це питання настільки часто обговорювалося в розсилках 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 + ba ||= 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буде виведено значення 3ie 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нулі перед кожним завданням.