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 są 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.