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 an'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 ||= truene fait pas ce que votre réponse dit qu'il fait une "nuance".
a ||= best un opérateur d'affectation conditionnelle . Cela signifie que si aest indéfini ou faux , alors évaluez bet définissez ale résultat . De manière équivalente, si aest défini et évalué comme véridique, alors bn'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 + ba ||= b se traduit à peu près par a || a = bC'est un raccourci pour a || a = b. La différence est que, quand aest indéfini, a || a = baugmenterait NameError, alors qu'il se a ||= bmet aà b. Cette distinction est sans importance si aet bsont 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] || 2contre h[1] || h[1] = 2. Les deux expressions sont évaluées 0mais 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 = bun NameErrorif an'est pas défini. a ||= bne le fait pas, mais l'initialise aet 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 || bet a ||= bque je sache, c'est que si a=est une méthode, elle sera appelée indépendamment de ce qui aretourne. En outre, la seule différence entre a = b unless aet a ||= bque je sache, c'est que cette déclaration est évaluée au nillieu de asi aest 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.
aest 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 endEt if a then a = a else a = b endjetteront une erreur si aest non défini, alors a ||= bet a = a || bnon. En outre, a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : bet if a then a = a else a = b endévaluer adeux fois quand aest truthy, alors a ||= bet a = a || bne sont pas.
a || a = bn'évaluera pas adeux fois quand ac'est vrai.
the end state will be equivalent after the whole line has been evaluatedCe n'est pas nécessairement vrai cependant. Et si ac'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 ||= breviendra 6, mais self.a ? self.a : self.a = breviendra 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 falseet nil, ce qui pourrait ne pas être pertinent pour current_user, mais surtout le falsepeut ê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 ||= bsignifie «si aest indéfini ou faux ( falseou nil), défini asur bet évalué à (c.-à-d. Retour) b, sinon évaluez àa ».
D'autres essaient souvent d'illustrer cela en disant que cela a ||= béquivaut à a || a = bou 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 as'agit d'une variable locale non définie. Dans ce cas, a ||= bsera défini asur b(et évalué à b), tandis que a || a = baugmentera 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 aest véridique. Dans ce cas, a ||= bne fera rien (autre que d'évaluer a), tandis que a = a || bfera appel a=(a)au arécepteur de. Comme d' autres l' ont souligné, cela peut faire une différence lorsque l'appel a=aa 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 avéracité. Dans ce cas, a = b unless aévaluera à nil(mais ane 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_missingméthode existe qui renvoie une valeur véridique pour a. Dans ce cas, a ||= bévaluera à tout method_missingretour et ne tentera pas de définir a, tandis que defined?(a) ? (a || a = b) : (a = b)définira asur bet é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 ||= bc'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 ||= bn'est pas équivalent à a || a = bquand aest une variable locale non définie? Eh bien, a = nil if falseassure que ce an'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
aest 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 afaut 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 ||= bréponse que j'ai trouvée sur Internet. Merci.
Supposer a = 2 etb = 3
ALORS, a ||= b sera ramené à ala valeur de2 .
Comme quand un évalue à une certaine valeur non résultée en falseou nil.. C'est pourquoi il lln'évalue pasb la valeur de.
Supposons maintenant a = niletb = 3 .
Ensuite, a ||= bil en résultera 3ieb .
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_usern'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
nilou falsepas seulementnil
Si Xn'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, baucune valeur ne sera affectée à a. aaura 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 @usersvariable 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 xest maintenant égal à 10. Cependant, dans le deuxième exemple xest déjà défini comme 20. L'opérateur conditionnel n'a donc aucun effet. xa encore 20 ans après avoir couru x ||= 10.
a ||= brevient à 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 aest 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=nilzéro avant chaque affectation.