Vérifier si une variable est définie?

581

Comment puis-je vérifier si une variable est définie dans Ruby? Existe-t-il une issetméthode de type disponible?

Lecture seulement
la source

Réponses:

791

Utilisez le defined?mot - clé ( documentation ). Il renverra une chaîne avec le type de l'élément, ou nils'il n'existe pas.

>> a = 1
 => 1
>> defined? a
 => "local-variable"
>> defined? b
 => nil
>> defined? nil
 => "nil"
>> defined? String
 => "constant"
>> defined? 1
 => "expression"

Comme l'a souligné skalee: "Il convient de noter que la variable définie sur zéro est initialisée."

>> n = nil  
>> defined? n
 => "local-variable"
Ricardo Acras
la source
92
Il convient de noter que la variable qui est définie sur nil est initialisée.
skalee
7
Si vous souhaitez définir une variable si elle n'existe pas et la laisser seule si c'est le cas, voir la réponse de @ danmayer (impliquant l' ||=opérateur) ci-dessous.
jrdioko
2
Voici une autre bizarrerie dans laquelle je peux entrer. Si vous définissez une variable dans un bloc if pour lequel la condition n'est jamais remplie, defined?renvoie toujours vrai pour une variable définie dans ce bloc!
elsurudo
1
Existe-t-il une méthode comme defined?celle -là qui retourne booléen?
stevec
Pour retourner vrai / faux,!!defined?(object_name)
stevec
91

Ceci est utile si vous ne voulez rien faire s'il existe mais le créer s'il n'existe pas.

def get_var
  @var ||= SomeClass.new()
end

Cela ne crée la nouvelle instance qu'une seule fois. Après cela, il continue de renvoyer la var.

danmayer
la source
9
C'est un Ruby très idiomatique aussi et très typique, soit dit en passant.
jrdioko
38
Ne l'utilisez pas ||=avec des valeurs booléennes, de peur de ressentir la douleur de la confusion.
Andrew Marshall
6
avec ce que @AndrewMarshall a dit, évitez cet idiome avec tout ce qui pourrait également revenir nilà moins que vous ne vouliez vraiment évaluer l'expression chaque fois qu'elle est appelée quand elle revientnil
nzifnab
1
Si vous êtes travaillez avec booléens, et que vous voulez la valeur par défaut est vrai si la variable n'a pas été explicitement définie sur false, vous pouvez utiliser cette construction: var = (var or var.nil?)
Tony Zito
1
@ArnaudMeuret En quelque sorte , pas vraiment. - même si cela ne semble pas identique, cela vaut la peine de lire les réponses à cette question.
Fund Monica's Lawsuit
70

La syntaxe correcte pour l'instruction ci-dessus est:

if (defined?(var)).nil? # will now return true or false
 print "var is not defined\n".color(:red)
else
 print "var is defined\n".color(:green)
end

en remplaçant ( var) par votre variable. Cette syntaxe renverra une valeur vraie / fausse pour évaluation dans l'instruction if.

foomip
la source
11
Ce n'est pas nécessaire car nul est évalué à faux lorsqu'il est utilisé dans un test
Jerome
Pourquoi ne pas defined?(var) == nil?
vol7ron
@ vol7ron - C'est une syntaxe parfaitement valide. L'utilisation de l'appel à .nil?est plus idiomatique, comme on dit. Il est plus "orienté objet" de demander à un objet s'il l'est plutôt nilque d'utiliser un opérateur de comparaison. Aucun n'est difficile à lire, alors utilisez celui qui vous aide à expédier plus de produits.
juanpaco
À quelle déclaration faites-vous référence?!? N'utilisez pas une réponse comme commentaire à autre chose.
Arnaud Meuret
18

defined?(your_var)marchera. Selon ce que vous faites, vous pouvez également faire quelque chose commeyour_var.nil?

digitalsanctum
la source
+1 your_var.nil?car il renvoie vrai ou faux et est beaucoup plus agréable à lire et à écrire que defined? var. Merci pour cela.
kakubei
28
your_var.nil?entraînera une erreur: undefined local variable or method your_varlorsqu'il n'est pas défini avant ...
Gobol
16

Essayez "à moins que" au lieu de "si"

a = "apple"
# Note that b is not declared
c = nil

unless defined? a
    puts "a is not defined"
end

unless defined? b
    puts "b is not defined"
end

unless defined? c
    puts "c is not defined"
end
user761856
la source
Qu'est-ce que cette réponse ajoute qui n'a pas été dit par les autres réponses?
Andrew Grimm
2
Cela répond très bien à la question d'une manière plus utile, n'est-ce pas?
regardez
2
Le guide du style rubis dit "Favorisez à moins que ce ne soit fini si pour des conditions négatives" github.com/bbatsov/ruby-style-guide
ChrisPhoenix
9

Utilisez defined? YourVariable
Keep it simple idiot ..;)

Saqib R.
la source
8

Voici du code, rien de sorcier mais ça marche assez bien

require 'rubygems'
require 'rainbow'
if defined?(var).nil?  # .nil? is optional but might make for clearer intent.
 print "var is not defined\n".color(:red)
else
 print "car is defined\n".color(:green)
end

De toute évidence, le code de coloration n'est pas nécessaire, juste une belle visualisation dans cet exemple de jouet.

Sardathrion - contre les abus SE
la source
Vraisemblablement parce que le nil?est facultatif.
James
8

AVERTISSEMENT concernant un motif de rubis commun

C'est la réponse clé: la defined?méthode. La réponse acceptée ci-dessus illustre parfaitement cela.

Mais il y a un requin qui se cache sous les vagues ...

Considérez ce type de motif rubis commun:

 def method1
    @x ||= method2
 end

 def method2
    nil
 end

method2revient toujours nil. La première fois que vous appelez method1, la @xvariable n'est pas définie - elle method2sera donc exécutée. et method2se mettra @xà nil. C'est bien, et tout va bien. Mais que se passe-t-il la deuxième fois que vous appelez method1?

N'oubliez pas que @x a déjà été défini sur zéro. But method2sera toujours exécuté à nouveau !! Si la méthode 2 est une entreprise coûteuse, ce n'est peut-être pas quelque chose que vous souhaitez.

Laissez la defined?méthode venir à la rescousse - avec cette solution, ce cas particulier est traité - utilisez ce qui suit:

  def method1
    return @x if defined? @x
    @x = method2
  end

Le diable est dans les détails: mais vous pouvez échapper à ce requin qui se cache avec la defined?méthode.

BKSpurgeon
la source
Vous avez tout à fait raison, merci d'avoir souligné cette mise en garde spécifique
Kulgar
5

Tu peux essayer:

unless defined?(var)
  #ruby code goes here
end
=> true

Parce qu'il renvoie un booléen.

Bruno Barros
la source
SyntaxError: compile error (irb):2: syntax error, unexpected $end, expecting kEND
Andrew Grimm
utiliser une unlessdéclaration semble trop compliqué
johannes
5

Comme de nombreux autres exemples le montrent, vous n'avez pas réellement besoin d'un booléen à partir d'une méthode pour faire des choix logiques en rubis. Ce serait une mauvaise forme de tout contraindre à un booléen, sauf si vous avez réellement besoin d'un booléen.

Mais si vous avez absolument besoin d'un booléen. Utilisation !! (bang bang) ou "falsy falsy révèle la vérité".

 irb
>> a = nil
=> nil
>> defined?(a)
=> "local-variable"
>> defined?(b)
=> nil
>> !!defined?(a)
=> true
>> !!defined?(b)
=> false

Pourquoi il n'est généralement pas avantageux de contraindre:

>> (!!defined?(a) ? "var is defined".colorize(:green) : "var is not defined".colorize(:red)) == (defined?(a) ? "var is defined".colorize(:green) : "var is not defined".colorize(:red))
=> true

Voici un exemple où cela est important car il repose sur la contrainte implicite de la valeur booléenne à sa représentation sous forme de chaîne.

>> puts "var is defined? #{!!defined?(a)} vs #{defined?(a)}"
var is defined? true vs local-variable
=> nil
donnoman
la source
3

Il convient de mentionner que l’utilisation de defined pour vérifier si un champ spécifique est défini dans un hachage peut se comporter de manière inattendue:

var = {}
if defined? var['unknown']
  puts 'this is unexpected'
end
# will output "this is unexpected"

La syntaxe est correcte ici, mais defined? var['unknown'] sera évaluée dans la chaîne "method", donc le ifbloc sera exécuté

edit: La notation correcte pour vérifier si une clé existe dans un hachage serait:

if var.key?('unknown')
leberknecht
la source
2

Veuillez noter la distinction entre "défini" et "attribué".

$ ruby -e 'def f; if 1>2; x=99; end;p x, defined? x; end;f'
nil
"local-variable"

x est défini même s'il n'est jamais attribué!

Robert Klemme
la source
C'est quelque chose que je viens de découvrir. Je m'attendais NameError Exception: undefined local variable or methodet j'étais confus quand la seule affectation / mention de la variable était dans un bloc if qui n'était pas touché.
Paul Pettengill
0

En outre, vous pouvez vérifier s'il est défini dans une chaîne via une interpolation, si vous codez:

puts "Is array1 defined and what type is it? #{defined?(@array1)}"

Le système vous indiquera le type s'il est défini. S'il n'est pas défini, il renverra simplement un avertissement indiquant que la variable n'est pas initialisée.

J'espère que cela t'aides! :)

Elliott
la source
0

defined?est génial, mais si vous êtes dans un environnement Rails, vous pouvez également l'utiliser try, en particulier dans les cas où vous souhaitez vérifier un nom de variable dynamique:

foo = 1
my_foo = "foo"
my_bar = "bar"
try(:foo)        # => 1
try(:bar)        # => nil
try(my_foo)      # => 1
try(my_bar)      # => nil
John Donner
la source