Que signifie le code suivant dans Ruby?
||=
A-t-il un sens ou une raison à la syntaxe?
Cette question a été discutée si souvent sur les listes de diffusion Ruby et les blogs Ruby qu'il y a maintenant même des threads sur la liste de diffusion Ruby dont le seul but est de collecter des liens vers tous les autres threads de la liste de diffusion Ruby qui traitent de ce problème. .
En voici une: la liste définitive des fils et des pages || = (OR Equal)
Si vous voulez vraiment savoir ce qui se passe, jetez un œil à la section 11.4.2.3 «Affectations abrégées» du projet de spécification du langage Ruby .
En première approximation,
a ||= b
est équivalent à
a || a = b
et non équivalent à
a = a || b
Cependant, ce n'est qu'une première approximation, surtout si elle a
n'est pas définie. La sémantique diffère également selon qu'il s'agit d'une affectation de variable simple, 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 = false; a ||= true
ne fait pas ce que votre réponse dit qu'il fait une "nuance".a ||= b
est un opérateur d'affectation conditionnelle . Cela signifie que sia
est indéfini ou faux , alors évaluezb
et définisseza
le résultat . De manière équivalente, sia
est défini et évalué comme véridique, alorsb
n'est pas évalué et aucune affectation n'a lieu. Par exemple:Confusément, il ressemble à d'autres opérateurs d'affectation (tels que
+=
), mais se comporte différemment.a += b
Se traduit para = a + b
a ||= b
se traduit à peu près para || a = b
C'est un raccourci pour
a || a = b
. La différence est que, quanda
est indéfini,a || a = b
augmenteraitNameError
, alors qu'il sea ||= b
meta
àb
. Cette distinction est sans importance sia
etb
sont toutes deux des variables locales, mais elle est significative si l'une ou l'autre est une méthode getter / setter d'une classe.Lectures complémentaires:
la source
h = Hash.new(0); h[1] ||= 2
. Considérons maintenant les deux extensions possiblesh[1] = h[1] || 2
contreh[1] || h[1] = 2
. Les deux expressions sont évaluées0
mais la première augmente inutilement la taille du hachage. C'est peut-être pour cela que Matz a choisi de se||=
comporter davantage comme la deuxième extension. (J'ai basé ceci sur un exemple d'un des fils liés à dans une autre réponse.)a || a = b
unNameError
ifa
n'est pas défini.a ||= b
ne le fait pas, mais l'initialisea
et le définit à la placeb
. Pour autant que je sache, c'est la seule distinction entre les deux. De même, la seule différence entrea = a || b
eta ||= b
que je sache, c'est que sia=
est une méthode, elle sera appelée indépendamment de ce quia
retourne. En outre, la seule différence entrea = b unless a
eta ||= b
que je sache, c'est que cette déclaration est évaluée aunil
lieu dea
sia
est véridique. Beaucoup d'approximations, mais rien de bien équivalent ...Réponse concise et complète
évalue la même manière que chacune des lignes suivantes
-
D'autre part,
évalue la même manière que chacune des lignes suivantes
-
Edit: Comme AJedi32 l'a souligné dans les commentaires, cela n'est vrai que si: 1. a est une variable définie. 2. L'évaluation d'une seule fois et de deux fois n'entraîne pas de différence dans l'état du programme ou du système.
la source
a
est faux / zéro / non défini, il est évalué deux fois. (Mais je ne connais pas Ruby, donc je ne sais pas si les valeurs peuvent être 'évaluées' exactement ...)a || a = b
,a ? a : a = b
,if a then a else a = b end
Etif a then a = a else a = b end
jetteront une erreur sia
est non défini, alorsa ||= b
eta = a || b
non. En outre,a || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
etif a then a = a else a = b end
évaluera
deux fois quanda
est truthy, alorsa ||= b
eta = a || b
ne sont pas.a || a = b
n'évaluera pasa
deux fois quanda
c'est vrai.the end state will be equivalent after the whole line has been evaluated
Ce n'est pas nécessairement vrai cependant. Et sia
c'est une méthode? Les méthodes peuvent avoir des effets secondaires. Par exemple, avecpublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
,self.a ||= b
reviendra 6, maisself.a ? self.a : self.a = b
reviendra 7.En bref,
a||=b
signifie: Sia
estundefined, nil or false
, attribuezb
àa
. Sinon, gardeza
intact.la source
x ||= y
veux diresi
x
a une valeur laisser seul et ne change pas la valeur, sinon misx
ày
la source
Cela signifie ou égal à. Il vérifie si la valeur de gauche est définie, puis utilise cela. Si ce n'est pas le cas, utilisez la valeur à droite. Vous pouvez l'utiliser dans Rails pour mettre en cache les variables d'instance dans les modèles.
Un exemple rapide basé sur Rails, où nous créons une fonction pour récupérer l'utilisateur actuellement connecté:
Il vérifie si la variable d'instance @current_user est définie. Si tel est le cas, il le renverra, économisant ainsi un appel de base de données. Si ce n'est pas le cas, nous appelons puis définissons la variable @current_user sur cela. C'est une technique de mise en cache très simple, mais elle est idéale lorsque vous récupérez plusieurs fois la même variable d'instance dans l'application.
la source
undefined
, mais aussi surfalse
etnil
, ce qui pourrait ne pas être pertinent pourcurrent_user
, mais surtout lefalse
peut être inattendu dans d'autres casest
"si x est faux ou indéfini, alors x pointe vers y"
la source
Pour être précis,
a ||= b
signifie «sia
est indéfini ou faux (false
ounil
), définia
surb
et évalué à (c.-à-d. Retour)b
, sinon évaluez àa
».D'autres essaient souvent d'illustrer cela en disant que cela
a ||= b
équivaut àa || a = b
oua = a || b
. Ces équivalences peuvent être utiles pour comprendre le concept, mais sachez qu'elles ne sont pas exactes dans toutes les conditions. Permettez-moi d'expliquer:a ||= b
⇔a || a = b
?Le comportement de ces instructions diffère lorsqu'il
a
s'agit d'une variable locale non définie. Dans ce cas,a ||= b
sera définia
surb
(et évalué àb
), tandis quea || a = b
augmenteraNameError: undefined local variable or method 'a' for main:Object
.a ||= b
⇔a = a || b
?L'équivalence de ces déclarations sont souvent considérées, car une équivalence similaire est vrai pour d' autres affectations abrégé opérateurs ( par exemple
+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
et>>=
). Cependant,||=
le comportement de ces déclarations peut différer quanda=
est une méthode sur un objet eta
est véridique. Dans ce cas,a ||= b
ne fera rien (autre que d'évaluera
), tandis quea = a || b
fera appela=(a)
aua
récepteur de. Comme d' autres l' ont souligné, cela peut faire une différence lorsque l'appela=a
a des effets secondaires, comme l'ajout de clés à un hachage.a ||= b
⇔a = b unless a
??Le comportement de ces déclarations ne diffère que par ce à quoi elles évaluent la
a
véracité. Dans ce cas,a = b unless a
évaluera ànil
(maisa
ne sera toujours pas défini, comme prévu), tandis quea ||= b
évaluera àa
.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????Toujours pas. Ces instructions peuvent différer lorsqu'une
method_missing
méthode existe qui renvoie une valeur véridique poura
. Dans ce cas,a ||= b
évaluera à toutmethod_missing
retour et ne tentera pas de définira
, tandis quedefined?(a) ? (a || a = b) : (a = b)
définiraa
surb
et évaluera surb
.D'accord, d'accord, alors qu'est - ce
a ||= b
équivalent? Existe-t-il un moyen d'exprimer cela en Ruby?Eh bien, en supposant que je ne néglige rien, je pense que
a ||= b
c'est fonctionnellement équivalent à ... ( drumroll )Attendez! N'est-ce pas juste le premier exemple avec un noop devant lui? Enfin, pas tout à fait. Rappelez-vous comment j'ai dit auparavant que ce
a ||= b
n'est pas équivalent àa || a = b
quanda
est une variable locale non définie? Eh bien,a = nil if false
assure que cea
n'est jamais indéfini, même si cette ligne n'est jamais exécutée. Les variables locales dans Ruby ont une portée lexicale.la source
(a=b unless a) or a
a
est une méthode, elle sera appelée deux fois au lieu d'une fois (si elle retourne une valeur véridique la première fois). Cela pourrait entraîner des comportements différents si, par exemple, ila
faut du temps pour revenir ou s'il a des effets secondaires.b
àa
, le rhs n'assigne-t-il pas encore au lhs, ou en d'autres termes, le lhs ne fixe-t-il toujours pas sa valeur au rhs?a ||= b
réponse que j'ai trouvée sur Internet. Merci.unless x x = y end
sauf si x a une valeur (elle n'est ni nulle ni fausse), définissez-la égale à y
est équivalent à
x ||= y
la source
Supposer
a = 2
etb = 3
ALORS,
a ||= b
sera ramené àa
la valeur de2
.Comme quand un évalue à une certaine valeur non résultée en
false
ounil
.. C'est pourquoi illl
n'évalue pasb
la valeur de.Supposons maintenant
a = nil
etb = 3
.Ensuite,
a ||= b
il en résultera3
ieb
.Comme il essaie d'abord d'évaluer la valeur de a qui a abouti à
nil
.. il a donc évaluéb
la valeur deLe meilleur exemple utilisé dans l'application ror est:
Où,
User.find_by_id(session[:user_id])
est renvoyé si et seulement si@current_user
n'est pas initialisé auparavant.la source
a || = b
Signifie si une valeur est présente dans 'a' et que vous ne voulez pas la modifier en utilisant cette valeur, sinon si 'a' n'a pas de valeur, utilisez la valeur de 'b'.
Des mots simples, si côté gauche sinon nul, pointent vers la valeur existante, sinon pointent vers la valeur du côté droit.
la source
est équivalent à
et pas
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)
si tu utilises:
a est toujours:
mais quand tu l'écris comme ça:
a devient:
parce que vous avez attribué la valeur de lui-même à la clé
10
, qui est par défaut true, donc maintenant le hachage est défini pour la clé10
, plutôt que de ne jamais effectuer l'affectation en premier lieu.la source
C'est comme une instanciation paresseuse. Si la variable est déjà définie, elle prendra cette valeur au lieu de la recréer.
la source
N'oubliez pas que ce
||=
n'est pas une opération atomique et donc, ce n'est pas sûr pour les threads. En règle générale, ne l'utilisez pas pour les méthodes de classe.la source
Ceci est la notation d'affectation par défaut
par exemple: x || = 1,
cela vérifiera si x est nul ou non. Si x est bien nul, il lui attribuera alors cette nouvelle valeur (1 dans notre exemple)
plus explicite:
si x == nul
x = 1
fin
la source
nil
oufalse
pas seulementnil
|| = est un opérateur d'affectation conditionnelle
est équivalent à
Ou bien
la source
Si
X
n'a PAS de valeur, la valeur de lui sera attribuéeY
. Sinon, il conservera sa valeur d'origine, 5 dans cet exemple:la source
Comme une idée fausse commune,
a ||= b
n'est pas équivalent àa = a || b
, mais il se comporte commea || a = b
.Mais voici un cas délicat. Si
a
n'est pas défini,a || a = 42
augmenteNameError
, tandis quea ||= 42
retourne42
. Donc, ils ne semblent pas être des expressions équivalentes.la source
||=
affecte la valeur à droite uniquement si left == nil (ou est indéfini ou faux).la source
Cette syntaxe ruby-lang. La bonne réponse est de vérifier la documentation de ruby-lang. Toutes les autres explications sont obscurcies .
Google
"Ruby-lang docs Affectation abrégée".
Documents Ruby-lang
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
la source
Parce que
a
était déjà réglé sur1
parce
a
étaitnil
la source
Cela se traduit par:
qui sera
donc finalement
Maintenant, si vous appelez à nouveau:
Maintenant, si vous appelez à nouveau:
Si vous observez,
b
aucune valeur ne sera affectée àa
.a
aura toujours5
.C'est un modèle de mémorisation utilisé dans Ruby pour accélérer les accesseurs.
Cela se traduit essentiellement par:
Vous allez donc appeler la base de données pour la première fois que vous appelez cette méthode.
Les appels futurs à cette méthode renverront simplement la valeur de la
@users
variable d'instance.la source
||=
est appelé un opérateur d'affectation conditionnelle.Cela fonctionne essentiellement comme
=
mais à l'exception que si une variable a déjà été affectée, elle ne fera rien.Premier exemple:
Deuxième exemple:
Dans le premier exemple
x
est maintenant égal à 10. Cependant, dans le deuxième exemplex
est déjà défini comme 20. L'opérateur conditionnel n'a donc aucun effet.x
a encore 20 ans après avoir courux ||= 10
.la source
a ||= b
revient à direa = b if a.nil?
oua = b unless a
Mais les 3 options affichent-elles les mêmes performances? Avec Ruby 2.5.1 cela
prend 0,099 secondes sur mon PC, tandis que
prend 0.062 Secondes. C'est presque 40% plus rapide.
et puis nous avons aussi:
ce qui prend 0,166 seconde.
Non pas que cela aura un impact significatif sur les performances en général, mais si vous avez besoin de ce dernier bit d'optimisation, considérez ce résultat. Soit dit en passant:
a = 1 unless a
est plus facile à lire pour le novice, il est explicite.Remarque 1: la raison pour laquelle la ligne d'affectation est répétée plusieurs fois est de réduire la surcharge de la boucle sur le temps mesuré.
Note 2: Les résultats sont similaires si je fais
a=nil
zéro avant chaque affectation.la source