Que signifie le code suivant en Ruby?
||=
At-il un sens ou une raison pour la syntaxe?
Cette question a été si souvent discutée sur les blogs Ruby et Ruby qu'il existe même des discussions sur la liste de diffusion Ruby dont le seul but est pour collecter des liens vers tous les autres sujets de la liste de diffusion Ruby qui traitent de ce problème.
En voici un: La liste définitive des fils et des pages (OU égaux)) ==
Si vous voulez vraiment savoir ce qui se passe, consultez la section 11.4.2.3 "Assignations abrégées" de Ruby Language Draft). Spécification .
En première approximation,
a ||= b
est équivalent à
a || a = b
et pas équivalent à
a = a || b
Cependant, il ne s’agit que d’une première approximation, en particulier si a
n’est pas défini. La sémantique diffère également selon qu’il s’agit d’une simple affectation de variable, d’une affectation de méthode ou d’une affectation d’indexation:
a ||= b
a.c ||= b
a[c] ||= b
sont tous traités différemment.
a ||= b
est un "opérateur d'assignation conditionnelle". Il s’agit de sort-de-mais-pas-tout à fait} _ (*) en abrégé pour a || a = b
.
Cela signifie "si a
est indéfini ou falsey (false
ou nil
), puis évaluez b
et affectez a
au résultat".
Par exemple:
> a ||= nil
=> nil
> a ||= 0;
=> 0
> a ||= 2;
=> 0
> foo = false;
=> false
> foo ||= true;
=> true
> foo ||= false;
=> true
L'évaluation de court-circuit de Ruby signifie que si a
est défini et évalué comme une vérité, le côté droit de l'opérateur n'est pas évalué et aucune affectation n'a lieu. Cette distinction est sans importance si a
et b
sont toutes les deux des variables locales, mais est significative si l'une ou l'autre est une méthode getter/setter d'une classe.
De manière confuse, il ressemble à d’autres opérateurs d’attribution (tels que +=
) mais se comporte différemment.
a += b
se traduit par a = a + b
a ||= b
se traduit approximativement par * a || a = b
* Sauf que, lorsque a
n'est pas défini, a || a = b
serait NameError, alors que a ||= b
définit a
sur b
.
Lectures complémentaires:
a ||= b
évalue de la même manière que each des lignes suivantes
a || a = b
a ? a : a = b
if a then a else a = b end
-
D'autre part,
a = a || b
évalue de la même manière que each des lignes suivantes
a = a ? a : b
if a then a = a else a = b end
-
Modifier: comme AJedi32 l’a souligné dans les commentaires, cela n’est valable que si: 1. a est une variable définie. 2. L'évaluation une fois et deux fois n'entraîne aucune différence dans l'état du programme ou du système.
En bref, a||=b
signifie: Si a
est undefined, nil or false
, affectez b
à a
. Sinon, laissez a
intact.
x ||= y
signifie
si x
a une valeur, laissez-la tranquille et ne changez pas la valeur, sinon définissez x
sur y
.
Cela signifie ou égal à. Il vérifie si la valeur à gauche est définie, puis l'utilise. Si ce n'est pas le cas, utilisez la valeur à droite. Vous pouvez l'utiliser dans Rails pour mettre en cache des variables d'instance dans des modèles.
Un exemple rapide basé sur Rails, dans lequel nous créons une fonction pour extraire l'utilisateur actuellement connecté:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
Il vérifie si la variable d'instance @current_user est définie. Si c'est le cas, il le renverra, enregistrant ainsi un appel à la base de données. Si ce n'est pas défini cependant, nous passons l'appel et nous affectons ensuite la variable @current_user à celle C'est une technique de mise en cache très simple, mais idéale pour extraire plusieurs fois la même variable d'instance dans l'application.
x ||= y
est
x || x = y
"si x est faux ou non défini, alors x pointe vers y"
Supposons que a = 2
et b = 3
ALORS, a ||= b
se traduira par la valeur de a
c'est-à-dire 2
.
Comme lorsque a évalue une valeur qui n’a pas pour résultat false
ou nil
.. C’est pourquoi ll
n’évalue pas la valeur de b
.
Supposons maintenant a = nil
et b = 3
.
Ensuite, a ||= b
sera converti en 3
c'est-à-dire b
.
Comme il essaie d’abord d’évaluer la valeur de a qui a pour résultat nil
.., il a donc évalué la valeur de b
.
Le meilleur exemple utilisé dans ror app est:
#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
Où, User.find_by_id(session[:user_id])
est déclenché si et seulement si @current_user
n'est pas initialisé auparavant.
unless x
x = y
end
sauf si x a une valeur (ce n'est ni nul ni faux), définissez-la égale à y
est équivalent à
x ||= y
C'est la notation d'attribution par défaut
par exemple: x || = 1
Cela vérifiera si x est nul ou non. Si x est bien nul, il lui attribue cette nouvelle valeur (1 dans notre exemple)
plus explicite:
si x == nul
x = 1
fin
C'est comme une instanciation paresseuse . Si la variable est déjà définie, elle prendra cette valeur au lieu de la créer à nouveau.
a ||= b
est équivalent à
a || a = b
et pas
a = a || b
en raison de la situation où vous définissez un hachage avec une valeur par défaut (le hachage renverra la valeur par défaut pour toutes les clés non définies)
a = Hash.new(true) #Which is: {}
si tu utilises:
a[10] ||= 10 #same as a[10] || a[10] = 10
a est encore:
{}
mais quand vous écrivez comme ça:
a[10] = a[10] || 10
a devient:
{10 => true}
parce que vous avez attribué la valeur de celle-ci à la clé 10
, la valeur par défaut étant true, le hachage est maintenant défini pour la clé 10
, plutôt que de ne jamais exécuter l'affectation à la place.
b = 5
a ||= b
Cela se traduit par:
a = a || b
qui sera
a = nil || 5
alors finalement
a = 5
Maintenant, si vous appelez ceci à nouveau:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Maintenant, si vous appelez ceci à nouveau:
a ||= b
a = a || b
a = 5 || 6
a = 5
Si vous observez, la valeur b
ne sera pas affectée à a
. a
aura toujours 5
.
C'est un modèle de mémorisation utilisé dans Ruby pour accélérer les accesseurs.
def users
@users ||= User.all
end
Cela se traduit essentiellement par:
@users = @users || User.all
Vous allez donc appeler la base de données pour la première fois.
Les prochains appels à cette méthode renverront simplement la valeur de la variable d'instance @users
.
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
Parce que a
était déjà défini sur 1
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
Parce que a
était nil
Rappelez-vous également que ||=
n'est pas une opération atomique et donc, il n'est pas thread-safe En règle générale, ne l'utilisez pas pour les méthodes de classe.
|| = est un opérateur d'affectation conditionnel
x ||= y
est équivalent à
x = x || y
Ou bien
if defined?(x) and x
x = x
else
x = y
end
a || = b
Signifie que si une valeur est présente dans 'a' et que vous ne voulez pas la modifier, conservez cette valeur, sinon si 'a' n'a aucune valeur, utilisez la valeur de 'b'.
Les mots simples, s'ils sont à gauche sinon NULL, désignent une valeur existante, sinon une valeur à droite.
||=
est appelé opérateur d'assignation conditionnelle.
Cela fonctionne fondamentalement en tant que =
mais avec l'exception que si une variable a déjà été assignée elle ne fera rien.
Premier exemple:
x ||= 10
Deuxième exemple:
x = 20
x ||= 10
Dans le premier exemple, x
est maintenant égal à 10. Cependant, dans le deuxième exemple, x
est déjà défini à 20. Ainsi, l'opérateur conditionnel n'a aucun effet. x
a toujours 20 ans après l'exécution de x ||= 10
.
comme une idée fausse commune a || = b n'est pas équivalent à a = a || b mais c'est vrai, mais il se comporte comme un || a = b
Mais voici un cas délicat
Si a n'est pas défini, a || a = 42 soulève NameError, tandis que a || = 42 renvoie 42. Par conséquent, elles ne semblent pas être des expressions équivalentes.
a ||= b
équivaut à dire a = b if a.nil?
ou a = b unless a
Mais les 3 options affichent-elles les mêmes performances? Avec Ruby 2.5.1 cela
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
prend 0,099 secondes sur mon PC, tandis que
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
prend 0.062 secondes. C'est presque 40% plus rapide.
et puis nous avons aussi:
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
qui prend 0,166 secondes.
Cela n'aura pas un impact significatif sur les performances en général, mais si vous avez besoin de cette dernière optimisation, tenez compte de ce résultat… .. Au fait: a = 1 unless a
est plus facile à lire pour le novice, il se passe d'explications.
Remarque 1: la répétition répétée de la ligne d'affectation à plusieurs reprises a pour but de réduire le temps système de la boucle pendant le temps mesuré.
Note 2: Les résultats sont similaires si je fais a=nil
nil avant chaque affectation.
||=
attribue une valeur à droite uniquement si == nil (ou est indéfini ou faux).