Méfiez-vous des appels de méthode Ruby et des références de variables

Réimprimé de Article de blog.

Récemment, j'ai écrit quelques personnes qui en sont accro, je vais donc le résumer un instant.

Appel de méthode Ruby

Dans Ruby, lors de l'appel d'une méthode, vous pouvez "appeler la méthode en omettant` ()" "par rapport aux autres langages.

def hoge(a = nil)
  "#hoge(#{a})"
end

#Vous pouvez appeler une méthode sans parenthèses
p hoge
# => "#hoge()"

#Vous pouvez faire référence à la méthode même avec un appel de type méthode
p hoge()      # => "#hoge()"
p hoge 42     # => "#hoge(42)"
p self.hoge   # => "#hoge()"

Eh bien c'est vrai.

Que faire si un nom de variable portant le même nom que le nom de la méthode est défini?

Le problème est lorsque «méthodes et variables du même nom» sont mélangées. Dans ce cas, reportez-vous à la méthode "Variable si la variable peut être référencée" et "Sinon".

def hoge(a = nil)
  "#hoge(#{a})"
end

#Puisqu'aucune variable n'est définie à ce stade, la méthode est appelée avec priorité.
p hoge   # => #hoge()

#Définir des variables
hoge = 42

#Après avoir défini la variable, donnez la priorité à la variable.
p hoge   # => 42

#Dans un appel de type méthode, appelez la méthode
p hoge()      # => "#hoge()"
p hoge 42     # => "#hoge(42)"
p self.hoge   # => "#hoge()"

À ce stade, notez que "méthode si avant l'expression d'affectation" et "variable si avant l'expression d'affectation".

Quand les variables sont-elles définies?

Par exemple, dans Ruby, il y a un cas où le processus qui définit réellement la variable n'est pas appelé comme suit.

def hoge(a = nil)
  "#hoge(#{a})"
end

if false
  hoge = 42
end

#Est-ce une référence de méthode?
p hoge

Dans le langage d'interprétation, on peut s'attendre à ce que ce soit "la méthode est appelée parce que la variable n'est pas définie". Cependant, en réalité, «p hoge» fait référence à la «variable« hoge »». C'est un secret sur la façon dont Ruby exécute le code source. Dans Ruby, avant d'exécuter le code source, commencez par «analyser l'intégralité du code source Ruby», puis exécutez le code Ruby. Ainsi, comme le code ci-dessus, la variable hoge est implicite lorsque" l'expression d'affectation "est définie car tout le code source est analysé indépendamment de" si le code est appelé à l'exécution ". Il sera défini dans. Par conséquent, indépendamment du fait que le contenu de l'instruction if soit réellement appelé, la "variable hoge" sera référencée "en dehors de l'instruction if" lorsque "l'expression d'affectation" est définie.

#Résultat d'exécution réel
def hoge(a = nil)
  "#hoge(#{a})"
end

if false
  hoge = 42
end

#Variables de référence ici
#Renvoie nil car la valeur par défaut de la variable est nil
p hoge
# nil

Postfix si + définition de la variable

Il est très difficile de comprendre comment Ruby analyse le code source. Par exemple, que se passe-t-il si vous "définissez une variable hoge en faisant référence à hoge avec un suffixe if "comme suit?

hoge = 42 if hoge.nil?
p hoge

Beaucoup de gens pensent que c'est parce que ʻif hoge.nil? Est traité avant la définition de variable, donc le processus hoge = 42n'est pas réellement appelé. Cependant, en réalité, le codehoge = 42 if hoge.nil?Est analysé comme "ceci est un processus au total". Par conséquent, quand ʻif hoge.nil?Est appelé, le code hoge = 42 a déjà été analysé et le processus est "la variable" hoge` est définie ". Ainsi, le résultat de l'exécution du code ci-dessus est

hoge = 42 if hoge.nil?
p hoge
# => 42

Ensuite, " hoge = 42 "est traité. Par contre, s'il ne s'agit pas d'un suffixe if, le résultat sera différent, il faut donc faire attention.

#Cela entraînera une erreur
# error: undefined local variable or method `hoge' for main:Object (NameError)
if hoge.nil?
  hoge = 42
end

C'est parce que ʻif hoge.nil? `Est analysé avant la variable et l'expression conditionnelle de l'instruction if est exécutée" dans l'état où la variable n'est pas définie ".

Que se passe-t-il lorsque eval ("hoge")

Dans Ruby, il existe une méthode appelée «eval». C'est la méthode qui exécute le code source Ruby "au moment de l'exécution".

def hoge(a = nil)
  "#hoge(#{a})"
end

hoge = 42
#Exécutez la chaîne transmise à eval en tant que code Ruby
p eval("hoge + hoge")
# => 84

Lorsque le code ci-dessus est exécuté, «hoge + hoge» est exécuté après l'expression d'affectation, donc reportez-vous à «Variable». Alors, que se passe-t-il lorsque vous exécutez du code comme celui-ci:

def hoge(a = nil)
  "#hoge(#{a})"
end

#Exécuter hoge avant l'expression d'affectation
p eval("hoge")

hoge = 42

D'après le flux expliqué plus haut, "j'exécute" "hoge" "avant la variable", donc je m'attends à ce que ce soit un "appel de méthode". Cependant, lorsqu'il est réellement exécuté, le résultat est le suivant.

def hoge(a = nil)
  "#hoge(#{a})"
end

#Faire référence à des variables au lieu d'appels de méthode
p eval("hoge")
# => nil

hoge = 42

La raison de ce résultat est que "le timing du code source Ruby" et "le timing de l'exécution de ʻeval" sont différents. Le moment d'exécution de ʻeval est "au moment de l'exécution de Ruby". Ce "runtime" est "après que le code source de Ruby a été analysé". En d'autres termes, puisque "le moment d'exécution de ʻeval" "est déjà" après que le code source de Ruby a été analysé ", l'expression" hoge "se réfère à" avec priorité donnée aux variables ". Par conséquent, lors du référencement d'une variable ou d'une méthode à partir de ʻeval, la "variable" est préférentiellement appelée indépendamment de la "position de définition de l'expression d'affectation". Cela affecte également l'utilisation de binding.irb, ce qui pose un problème lors de l'appel de binding.irb" avant l'expression d'affectation ", par exemple:

def hoge(a = nil)
  "#hoge(#{a})"
end

#Liaison pour le débogage, etc..Démarrez irb à l'exécution avec irb
#Si vous faites référence à hoge sur cette console irb, vous verrez des "variables".
binding.irb

hoge = 42

binding.irb exécute le code Ruby entré en utilisant ʻeval. Par conséquent, il sera exécuté en se référant à la "variable" comme dans l'exemple précédent. Je pense qu'il est rare d'utiliser ʻeval dans le code Ruby normal, mais vous devez être prudent car utiliser binding.irb etc. signifie indirectement utiliser ʻeval`. ..

Résumé

  1. Dans Ruby, il est ambigu de savoir si l'expression " hoge "est un appel de méthode ou une référence de variable.
  2. Préférez la variable si elle existe, sinon donnez la priorité à l'appel de méthode
  1. Cependant, notez que lors du référencement dynamique d'une variable, la variable a la priorité.
  1. Fondamentalement, les noms de variables portant le même nom que le nom de la méthode doivent être évités
  1. Dans Ruby, il est important d'écrire du code en sachant s'il s'agit d'une "variable" ou d'une "méthode".

J'ai donc essayé de résumer les variables Ruby. Lorsque vous appelez une méthode avec binding.irb, nil peut être retourné, et si vous regardez attentivement le code, un nom de variable avec le même nom est défini après binding.irb. Il y avait une chose. Comme vous pouvez le voir, il y a quelques points addictifs dans Ruby, alors soyez prudent.

Recommended Posts

Méfiez-vous des appels de méthode Ruby et des références de variables
À propos des hachages et symboles Ruby
À propos de Ruby, modèle objet
À propos des classes et des instances Ruby
[Ruby] Questions et vérification du nombre d'arguments de méthode
À propos des guillemets simples et doubles Ruby
[Bases de Ruby] méthode split et méthode to_s
À propos de l'opérateur de produit Ruby (&) et de l'opérateur somme (|)
À propos de l'héritage orienté objet et du rendement Ruby
Soyez prudent lorsque vous omettez le retour dans Ruby
Différence entre la variable d'instance Ruby et la variable locale
[Ruby] Comment utiliser la méthode gsub et la sous-méthode
À propos de la synchronisation des appels et des arguments de la méthode addToBackStack
À propos de l'expression régulière utilisée dans la méthode ruby sub