2010-06-06 21 views
7

Lorsque a est indéfini, alors a || 1 va générer une erreur, mais pas a = a || 1. N'est-ce pas un peu inconsistant?Pourquoi dans Ruby, un || 1 va lancer une erreur quand `a` est indéfini, mais a = a || 1 ne le fera pas?

irb(main):001:0> a 
NameError: undefined local variable or method 'a' for main:Object 
     from (irb):1 
     from c:/ruby/bin/irb:12:in '<main>' 

irb(main):002:0> a || 1 
NameError: undefined local variable or method 'a' for main:Object 
     from (irb):2 
     from c:/ruby/bin/irb:12:in '<main>' 

irb(main):003:0> a = a || 1 
=> 1 
+0

Duplication de http://stackoverflow.com/questions/1462407/ruby-edge-cases –

Répondre

9
a 

Ici, vous évaluez a, ce qui n'est pas défini. Par conséquent, vous obtenez une exception.

a || 1 

Ici, vous encore doivent évaluer a pour déterminer la valeur de l'expression booléenne. Tout comme ci-dessus, a n'est pas défini. Par conséquent, vous obtenez une exception.

a = a || 1 

Ici, aest défini. Il est défini comme étant une variable locale non initialisée. Dans Ruby, les variables non initialisées sont évaluées à nil, donc le côté droit de l'expression d'affectation est nil || 1, ce qui donne 1, donc la valeur de retour de l'expression d'affectation est 1 et l'effet secondaire est que a est initialisé à 1.

EDIT: Il semble qu'il y ait une certaine confusion sur le moment où les variables sont définies et quand elles sont initialisées dans Ruby. Le get est défini à parse l'heure mais initialisé à runtime. Vous pouvez le voir ici:

foo # => NameError: undefined local variable or method `foo' for main:Object 

foo est indéfini.

if false 
    foo = 'This will never get executed' 
end 

À ce stade, foo est défini, même si la ligne ne sera jamais exécutée. Le fait que la ligne ne soit jamais exécutée est complètement hors de propos, car l'interpréteur n'a rien à voir avec cela: les variables locales sont définies par l'analyseur, et l'analyseur voit évidemment cette ligne.

foo # => nil 

Il n'y a pas d'erreur, parce que foo est défini, et il évalue à nil parce qu'il est non initialisée.

+0

+1, bien que je pense en disant "Il est défini comme une variable locale non initialisée." est un peu déroutant. Quand vous faites 'var = expr', vous ne définissez pas' var' pour qu'il ne soit pas initialisé. Vous définissez var comme étant la valeur de 'expr'. C'est juste que tandis que 'expr' évalue,' var' arrive à être non initialisé, ce qui signifie que toute référence à 'var' dans' expr' sera nulle. – sepp2k

+1

@ sepp2k: Ce sont deux étapes très distinctes. La définition de variable se produit dans l'analyseur, l'initialisation dans l'interpréteur. Selon l'implémentation de Ruby, il peut y avoir un temps significatif entre les deux événements. Dans BlueRuby, par exemple, Ruby sourcecode est analysé dans BRIL (BlueRuby Intermediate Language), qui est ensuite stocké dans une base de données, lorsque vous * installez * un programme Ruby. Cela pourrait être * années * jusqu'à ce que quelqu'un * exécute * le programme. Pendant tout ce temps, la variable a été définie mais pas initialisée. Les deux choses pourraient même arriver sur des machines différentes. –

1

Lorsque vous faites a || 1, vous demandez à chercher la valeur de a qui est définie.

Lorsque vous faites a = a || 1, vous lui demandez de rechercher la valeur de l'affectation de a à a qui ne semble pas indiquer d'erreur. Donc, bien que bizarre, je ne le crois pas incohérent.

+0

Ceci est faux.'a = a || 1' ne pas analyser comme '(a = a) || 1'. Il analyse comme 'a = (a || 1)', donc le résultat de "assigner un à un" n'a rien à voir avec ça, parce que a n'est jamais assigné à a. – sepp2k

+0

@ sepp2k: Je n'essayais pas d'impliquer cela du tout. Je suis désolé si je vous ai dérouté. Je crois que Jörg l'a expliqué plus clairement. – Cetra

0

Est-ce ce que vous voulez dire?

if !(defined? a) then 
    a = 1 
end 

Il peut être plus simple de déclarer la valeur avec 1 par défaut.