Sprawdzanie, czy zmienna jest zdefiniowana?


581

Jak mogę sprawdzić, czy zmienna jest zdefiniowana w Ruby? Czy dostępna jest issetmetoda typu?

Odpowiedzi:


791

Użyj defined?słowa kluczowego ( dokumentacja ). Zwróci ciąg znaków z rodzajem elementu lub niljeśli nie istnieje.

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

Jak zauważył skalee: „Warto zauważyć, że zmienna ustawiona na zero jest inicjalizowana”.

>> n = nil  
>> defined? n
 => "local-variable"

92
Warto zauważyć, że ustawiona zmienna nil jest inicjalizowana.
skalee

7
Jeśli chcesz ustawić zmienną, jeśli nie istnieje, i zostaw ją w spokoju, jeśli tak, zobacz odpowiedź @ danmayera (z udziałem ||=operatora) poniżej.
jrdioko

2
Oto kolejna osobliwość, do której mogę się zwrócić. Jeśli zdefiniujesz zmienną w bloku if, dla którego warunek nigdy nie jest spełniony, defined?nadal zwraca wartość true dla zmiennej zdefiniowanej w tym bloku!
elsurudo,

1
Czy istnieje metoda taka, defined?która zwraca wartość logiczną?
stevec

Aby zwrócić wartość prawda / fałsz,!!defined?(object_name)
stevec

91

Jest to przydatne, jeśli nie chcesz nic robić, jeśli istnieje, ale utwórz go, jeśli nie istnieje.

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

To tworzy nową instancję tylko raz. Potem po prostu zwraca var.


9
Nawiasem mówiąc, jest to bardzo idiomatyczny Ruby i bardzo typowy.
jrdioko

38
Po prostu nie używaj ||=wartości boolowskich, aby nie poczuć bólu pomieszania.
Andrew Marshall,

6
wraz z tym, co powiedział @AndrewMarshall, unikaj tego idiomu ze wszystkim, co mogłoby również powrócić, nilchyba że naprawdę chcesz oceniać wyrażenie za każdym razem, gdy jest wywoływane, gdy ono powracanil
nzifnab,

1
Jeśli pracy z logicznych i chcesz wartość domyślna aby mogło być prawdziwe, jeśli zmienna nie została jawnie ustawiona na false, można użyć tej budowy: var = (var or var.nil?)
Tony Zito

1
@ArnaudMeuret W pewnym sensie , nie bardzo. - choć może nie wydawać się identyczny, warto przeczytać odpowiedzi na to pytanie.
Pozew Fund Moniki z

70

Prawidłowa składnia powyższej instrukcji to:

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

podstawiając ( var) swoją zmienną. Ta składnia zwróci wartość prawda / fałsz do oceny w instrukcji if.


11
Nie jest to konieczne, ponieważ zero sprawdza się jako fałsz, gdy zostanie użyty w teście
Jerome,

Dlaczego nie defined?(var) == nil?
vol7ron

@ vol7ron - To jest całkowicie poprawna składnia. Używanie połączenia do .nil?jest bardziej idiomatyczne, jak mówią. Bardziej „obiektowo” jest pytać obiekt, czy jest nilto operator niż porównywanie. Żadna z tych rzeczy nie jest trudna do odczytania, więc użyj którejkolwiek, która pomoże Ci wysłać więcej produktów.
juanpaco

Do którego stwierdzenia się odwołujesz? Nie używaj odpowiedzi jako komentarza do czegoś innego.
Arnaud Meuret

18

defined?(your_var)będzie działać. W zależności od tego, co robisz, możesz także zrobić coś takiegoyour_var.nil?


+1 za, your_var.nil?ponieważ zwraca wartość prawda fałszu i jest o wiele ładniejszy do odczytu i zapisu niż defined? var. Dzięki za to.
kakubei

28
your_var.nil?spowoduje błąd: undefined local variable or method your_varjeśli nie zdefiniowano wcześniej ...
Gobol

16

Spróbuj „chyba” zamiast „jeśli”

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

Co dodaje ta odpowiedź, czego nie powiedzieli inne odpowiedzi?
Andrew Grimm

2
Bardzo ładnie odpowiada na pytanie w jeszcze bardziej użyteczny sposób, prawda?
patrz

2
Przewodnik w stylu ruby ​​mówi: „Sprzyjaj, chyba że ze względu na negatywne warunki” github.com/bbatsov/ruby-style-guide
ChrisPhoenix


8

Oto kod, nic, nauka rakietowa, ale działa wystarczająco dobrze

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

Oczywiście kod kolorowania nie jest konieczny, tylko ładna wizualizacja w tym przykładzie zabawki.


Prawdopodobnie dlatego, że nil?jest opcjonalny.
James

8

OSTRZEŻENIE Re: Wspólny wzór rubinowy

Oto kluczowa odpowiedź: defined?metoda. Powyższa odpowiedź doskonale to ilustruje.

Ale pod falami czai się rekin ...

Rozważ ten typ wspólnego wzoru rubinowego:

 def method1
    @x ||= method2
 end

 def method2
    nil
 end

method2zawsze wraca nil. Gdy po raz pierwszy nazywają method1The @xzmienna nie jest ustawiona - zatem method2będą działać. i method2ustawi @xna nil. W porządku, a wszystko dobrze i dobrze. Ale co się stanie, kiedy zadzwonisz po raz drugi method1?

Pamiętaj, że @x zostało już ustawione na zero. But method2nadal będzie uruchamiany ponownie !! Jeśli metoda 2 jest kosztownym przedsięwzięciem, może nie być to coś, czego chcesz.

Niech defined?metoda przyjdzie na ratunek - dzięki temu rozwiązaniu, że konkretny przypadek jest obsługiwany - użyj następujących poleceń:

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

Diabeł tkwi w szczegółach: ale tą defined?metodą możesz uniknąć tego czającego się rekina .


Masz całkowitą rację, dziękuję za podkreślenie tego szczególnego zastrzeżenia
Kulgar

5

Możesz spróbować:

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

Ponieważ zwraca wartość logiczną.


SyntaxError: compile error (irb):2: syntax error, unexpected $end, expecting kEND
Andrew Grimm,

użycie unlessoświadczenia wydaje się zbyt skomplikowane
John

5

Ponieważ wiele innych przykładów pokazuje, że tak naprawdę nie potrzebujesz logicznej metody, aby dokonać logicznych wyborów w Rubim. Byłoby kiepską formą przymuszanie wszystkiego do wartości logicznej, chyba że faktycznie potrzebujesz wartości logicznej.

Ale jeśli absolutnie potrzebujesz wartości logicznej. Posługiwać się !! (Bang Bang) lub „Falsy Falsy ujawnia prawdę”.

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

Dlaczego zwykle nie opłaca się wymuszać:

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

Oto przykład, w którym ma to znaczenie, ponieważ opiera się na domniemanym przymusie wartości logicznej do jego reprezentacji ciągu.

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

3

Należy wspomnieć, że użycie defineddo sprawdzenia, czy określone pole w haszowaniu może zachowywać się nieoczekiwanie:

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

Składnia jest tutaj poprawna, ale defined? var['unknown']zostanie przetworzona na ciąg znaków "method", więcif blok zostanie wykonany

edytuj: Poprawnym zapisem do sprawdzania, czy klucz istnieje w haszowaniu byłoby:

if var.key?('unknown')

2

Zwróć uwagę na rozróżnienie między „zdefiniowanym” a „przypisanym”.

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

x jest zdefiniowane, nawet jeśli nigdy nie jest przypisane!


Właśnie się z tym spotkałem. Spodziewałem się NameError Exception: undefined local variable or methodi byłem zdezorientowany, gdy jedynym przypisaniem / wzmianką o zmiennej był blok if, który nie został trafiony.
Paul Pettengill,

0

Możesz również sprawdzić, czy jest zdefiniowany w ciągu znaków za pomocą interpolacji, jeśli kodujesz:

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

System poinformuje o typie, jeśli jest zdefiniowany. Jeśli nie zostanie zdefiniowane, zwróci tylko ostrzeżenie, że zmienna nie została zainicjowana.

Mam nadzieję że to pomoże! :)


0

defined?jest świetny, ale jeśli jesteś w środowisku Railsów, możesz również użyć try, szczególnie w przypadkach, gdy chcesz sprawdzić nazwę zmiennej dynamicznej:

foo = 1
my_foo = "foo"
my_bar = "bar"
try(:foo)        # => 1
try(:bar)        # => nil
try(my_foo)      # => 1
try(my_bar)      # => nil
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.