Verificando se uma variável está definida?

581

Como posso verificar se uma variável está definida no Ruby? Existe um issetmétodo -type disponível?

Somente leitura
fonte

Respostas:

791

Use a defined?palavra - chave ( documentação ). Ele retornará uma String com o tipo do item, ou nilse ele não existir.

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

Como skalee comentou: "Vale a pena notar que a variável definida como zero é inicializada".

>> n = nil  
>> defined? n
 => "local-variable"
Ricardo Acras
fonte
92
Vale a pena notar que a variável que está definida como nil é inicializada.
skalee
7
Se você deseja definir uma variável, se ela não existir, e deixá-la em paz, se houver, consulte a resposta do @ danmayer (envolvendo o ||=operador) abaixo.
Jrdioko
2
Aqui está outra singularidade em que posso encontrar ... Se você definir uma variável em um bloco if para o qual a condição nunca é atendida, defined?ainda retornará true para uma variável definida dentro desse bloco!
elsurudo
1
Existe um método como defined?esse retorna booleano?
stevec 01/07/19
Para retornar verdadeiro / falso,!!defined?(object_name)
stevec 01/07/19
91

Isso é útil se você não quiser fazer nada, se existir, mas criá-lo, se não existir.

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

Isso cria apenas a nova instância uma vez. Depois disso, ele continua retornando o var.

danmayer
fonte
9
Isso também é Ruby muito idiomático e muito típico, a propósito.
Jrdioko
38
Apenas não use ||=com valores booleanos, para que você não sinta a dor da confusão.
Andrew Marshall
6
junto com o que @AndrewMarshall disse, evitar este idioma com qualquer coisa que possa voltar nilbem menos que você realmente quer para avaliar a expressão cada vez que é chamado quando ele retornarnil
nzifnab
1
Se você estiver trabalhando com booleanos e desejar que o valor padrão seja verdadeiro se a variável não tiver sido definida explicitamente como falsa, você poderá usar esta construção: var = (var or var.nil?)
Tony Zito
1
@ArnaudMeuret Mais ou menos , na verdade. - embora possa não parecer idêntico, vale a pena ler as respostas sobre essa pergunta.
Fund Monica's Lawsuit
70

A sintaxe correta para a instrução acima é:

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

substituindo ( var) por sua variável. Essa sintaxe retornará um valor verdadeiro / falso para avaliação na instrução if.

foomip
fonte
11
Isso não é necessário, como avalia nil false quando usado em um teste
Jerome
Por que não defined?(var) == nil?
precisa saber é o seguinte
@ vol7ron - Essa é uma sintaxe perfeitamente válida. Usar a chamada para .nil?é mais idiomático, como eles dizem. É mais "orientado a objetos" perguntar a um objeto se ele nildo que usar um operador de comparação. Não é difícil ler, portanto, use o que for necessário para enviar mais produtos.
juanpaco
A que declaração você está se referindo?!? Não use uma resposta como comentário para outra coisa.
Arnaud Meuret
18

defined?(your_var)vai funcionar. Dependendo do que você está fazendo, você também pode fazer algo comoyour_var.nil?

digitalsanctum
fonte
+1 your_var.nil?porque retorna true de false e é muito melhor ler e escrever do que defined? var. Obrigado por isso.
Kakubei
28
your_var.nil?resultará em erro: undefined local variable or method your_varquando não definido antes ...
Gobol
16

Tente "a menos que" em vez de "se"

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
fonte
O que essa resposta acrescenta que não foi dita pelas outras respostas?
Andrew Grimm
2
Responde muito bem à pergunta de uma maneira mais útil, não é?
veja
2
O guia de estilo ruby diz: "Favor não ser mais, se por condições negativas" github.com/bbatsov/ruby-style-guide
ChrisPhoenix
9

Use defined? YourVariable
Mantenha-o simples, bobo ..;)

Saqib R.
fonte
8

Aqui está algum código, nada de ciência de foguetes, mas funciona bem o suficiente

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

Claramente, o código de coloração não é necessário, apenas uma boa visualização neste exemplo de brinquedo.

Sardathrion - contra abuso SE
fonte
Presumivelmente porque o nil?é opcional.
James
8

AVISO Re: Um Padrão Ruby Comum

Esta é a resposta principal: o defined?método A resposta aceita acima ilustra isso perfeitamente.

Mas há um tubarão, escondido sob as ondas ...

Considere este tipo de padrão ruby ​​comum:

 def method1
    @x ||= method2
 end

 def method2
    nil
 end

method2sempre retorna nil. A primeira vez que você chama method1, a @xvariável não está definida - portanto method2, será executada. e method2será definido @xcomo nil. Tudo bem, tudo muito bem. Mas o que acontece na segunda vez que você ligamethod1 ?

Lembre-se de que @x já foi definido como nulo. But method2ainda será executado novamente !! Se o método2 for um empreendimento caro, isso pode não ser o desejado.

Deixe o defined?método ajudar - com esta solução, esse caso específico é tratado - use o seguinte:

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

O diabo está nos detalhes: mas você pode escapar desse tubarão à espreita com o defined?método.

BKSpurgeon
fonte
Você está totalmente certo, obrigado por destacar essa advertência específica
Kulgar
5

Podes tentar:

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

Porque retorna um valor booleano.

Bruno Barros
fonte
SyntaxError: compile error (irb):2: syntax error, unexpected $end, expecting kEND
Andrew Grimm
usar uma unlessdeclaração parece muito complicado
johannes
5

Como muitos outros exemplos mostram, você realmente não precisa de um booleano de um método para fazer escolhas lógicas em ruby. Seria uma forma ruim coagir tudo a um booleano, a menos que você realmente precise de um booleano.

Mas se você absolutamente precisa de um booleano. Usar !! (bang bang) ou "falsy falsy revela a verdade".

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

Por que geralmente não vale a pena coagir:

>> (!!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

Aqui está um exemplo em que isso é importante porque se baseia na coerção implícita do valor booleano à sua representação de string.

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

Deve-se mencionar que usar definedpara verificar se um campo específico está definido em um hash pode se comportar inesperadamente:

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

A sintaxe está correta aqui, mas defined? var['unknown']será avaliada para a sequência "method", para que o ifbloco seja executado

edit: A notação correta para verificar se existe uma chave em um hash seria:

if var.key?('unknown')
Leberknecht
fonte
2

Observe a distinção entre "definido" e "atribuído".

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

x é definido mesmo que nunca seja atribuído!

Robert Klemme
fonte
Isso é algo que me deparei. Eu estava esperando NameError Exception: undefined local variable or methode fiquei confuso quando a única atribuição / menção da variável estava em um bloco if que não estava sendo atingido.
Paul Pettengill
0

Além disso, você pode verificar se ele está definido enquanto está em uma string por interpolação, se você codificar:

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

O sistema informará o tipo, se estiver definido. Se não estiver definido, retornará apenas um aviso dizendo que a variável não foi inicializada.

Espero que isto ajude! :)

Elliott
fonte
0

defined?é ótimo, mas se você estiver em um ambiente Rails, também poderá usá-lo try, especialmente nos casos em que deseja verificar o nome de uma variável dinâmica:

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