Que signifie le code suivant dans Ruby?
||=
A-t-il un sens ou une raison à la syntaxe?
Que signifie le code suivant dans Ruby?
||=
A-t-il un sens ou une raison à la syntaxe?
Réponses:
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 si a
est indéfini ou faux , alors évaluez b
et définissez a
le résultat . De manière équivalente, si a
est défini et évalué comme véridique, alors b
n'est pas évalué et aucune affectation n'a lieu. Par exemple:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Confusément, il ressemble à d'autres opérateurs d'affectation (tels que +=
), mais se comporte différemment.
a += b
Se traduit par a = a + b
a ||= b
se traduit à peu près par a || a = b
C'est un raccourci pour a || a = b
. La différence est que, quand a
est indéfini, a || a = b
augmenterait NameError
, alors qu'il se a ||= b
met a
à b
. Cette distinction est sans importance si a
et b
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:
h = Hash.new(0); h[1] ||= 2
. Considérons maintenant les deux extensions possibles h[1] = h[1] || 2
contre h[1] || h[1] = 2
. Les deux expressions sont évaluées 0
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
un NameError
if a
n'est pas défini. a ||= b
ne le fait pas, mais l'initialise a
et le définit à la place b
. Pour autant que je sache, c'est la seule distinction entre les deux. De même, la seule différence entre a = a || b
et a ||= b
que je sache, c'est que si a=
est une méthode, elle sera appelée indépendamment de ce qui a
retourne. En outre, la seule différence entre a = b unless a
et a ||= b
que je sache, c'est que cette déclaration est évaluée au nil
lieu de a
si a
est véridique. Beaucoup d'approximations, mais rien de bien équivalent ...
a ||= b
évalue la même manière que chacune 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 la même manière que chacune des lignes suivantes
a = a ? a : b
if a then a = a else a = b end
-
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.
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
Et if a then a = a else a = b end
jetteront une erreur si a
est non défini, alors a ||= b
et a = a || b
non. En outre, a || a = b
, a ? a : a = b
, if a then a else a = b end
, a = a ? a : b
et if a then a = a else a = b end
évaluer a
deux fois quand a
est truthy, alors a ||= b
et a = a || b
ne sont pas.
a || a = b
n'évaluera pas a
deux fois quand a
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 si a
c'est une méthode? Les méthodes peuvent avoir des effets secondaires. Par exemple, avec public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
, self.a ||= b
reviendra 6, mais self.a ? self.a : self.a = b
reviendra 7.
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é:
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 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.
undefined
, mais aussi sur false
et nil
, ce qui pourrait ne pas être pertinent pour current_user
, mais surtout le false
peut être inattendu dans d'autres cas
x ||= y
est
x || x = y
"si x est faux ou indéfini, alors x pointe vers y"
Pour être précis, a ||= b
signifie «si a
est indéfini ou faux ( false
ou nil
), défini a
sur b
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
ou a = 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éfini a
sur b
(et évalué à b
), tandis que a || a = b
augmentera NameError: 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 quand a=
est une méthode sur un objet et a
est véridique. Dans ce cas, a ||= b
ne fera rien (autre que d'évaluer a
), tandis que a = a || b
fera appel a=(a)
au a
récepteur de. Comme d' autres l' ont souligné, cela peut faire une différence lorsque l'appel a=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
(mais a
ne sera toujours pas défini, comme prévu), tandis que a ||= 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 pour a
. Dans ce cas, a ||= b
évaluera à tout method_missing
retour et ne tentera pas de définir a
, tandis que defined?(a) ? (a || a = b) : (a = b)
définira a
sur b
et évaluera sur b
.
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 )
begin
a = nil if false
a || a = b
end
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
quand a
est une variable locale non définie? Eh bien, a = nil if false
assure que ce a
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.
(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, il a
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.
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
ou nil
.. C'est pourquoi il ll
n'évalue pasb
la valeur de.
Supposons maintenant a = nil
etb = 3
.
Ensuite, a ||= b
il en résultera 3
ieb
.
Comme il essaie d'abord d'évaluer la valeur de a qui a abouti à nil
.. il a donc évaluéb
la valeur de
Le meilleur exemple utilisé dans l'application ror 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 renvoyé si et seulement si @current_user
n'est pas initialisé auparavant.
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.
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 toujours:
{}
mais quand tu l'écris comme ça:
a[10] = a[10] || 10
a devient:
{10 => true}
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.
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.
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
nil
ou false
pas seulementnil
Si X
n'a PAS de valeur, la valeur de lui sera attribuée Y
. Sinon, il conservera sa valeur d'origine, 5 dans cet exemple:
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
||=
affecte la valeur à droite uniquement si left == nil (ou est indéfini ou faux).
Cette syntaxe ruby-lang. La bonne réponse est de vérifier la documentation de ruby-lang. Toutes les autres explications sont obscurcies .
"Ruby-lang docs Affectation abrégée".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
Cela se traduit par:
a = a || b
qui sera
a = nil || 5
donc finalement
a = 5
Maintenant, si vous appelez à nouveau:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Maintenant, si vous appelez à nouveau:
a ||= b
a = a || b
a = 5 || 6
a = 5
Si vous observez, b
aucune valeur ne sera 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 que vous appelez cette méthode.
Les appels futurs à cette méthode renverront simplement la valeur de la @users
variable d'instance.
||=
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:
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 comme 20. L'opérateur conditionnel n'a donc aucun effet. x
a encore 20 ans après avoir couru x ||= 10
.
a ||= b
revient à dire a = b if a.nil?
oua = 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
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.