2010-06-06 11 views
7

Gdy a jest niezdefiniowana, wówczas a || 1 wygeneruje błąd, ale a = a || 1 nie będzie. Czy to nie jest trochę niespójne?Dlaczego w Ruby, a || 1 spowoduje błąd, gdy "a" jest niezdefiniowane, ale a = a || 1 nie będzie?

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

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

Odpowiedz

9
a 

Tutaj oceniasz a, który nie jest zdefiniowany. Dlatego otrzymujesz wyjątek.

a || 1 

Tutaj nadal musiał ocenić a celu określenia wartości logicznej wypowiedzi. Podobnie jak wyżej, a nie jest zdefiniowany. Dlatego otrzymujesz wyjątek.

a = a || 1 

Tutaj ajest zdefiniowane. Jest zdefiniowana jako niezainicjowana zmienna lokalna. W Ruby niezainicjowane zmienne oceniają się na nil, więc prawa strona ekspresji przypisania ocenia na nil || 1, która ocenia na 1, więc wartość zwrotna przypisanej ekspresji wynosi 1, a efektem ubocznym jest to, że a jest inicjowane na 1.

EDYTOWANIE: Wydaje się, że jest pewne zamieszanie, gdy zmienne zostaną zdefiniowane i kiedy zostaną zainicjowane w Ruby. Wartość zdefiniowana w czas analizy, ale została zainicjowana w runtime. Możesz go zobaczyć tutaj:

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

foo jest niezdefiniowany.

W tym miejscu zdefiniowano foo, mimo że linia nigdy nie zostanie wykonana. Fakt, że linia nigdy nie zostanie wykonana, jest zupełnie nieistotny, ponieważ interpreter nie ma z tym nic wspólnego: zmienne lokalne są definiowane przez analizator składni, a analizator składni oczywiście widzi tę linię.

foo # => nil 

nie ma błędu, ponieważ foo jest zdefiniowana, a ocenia się nil ponieważ jest niezainicjowany.

+0

+1, chociaż myślę, że mówię "Zdefiniowano, że jest to niezainicjowana zmienna lokalna". jest nieco mylące. Kiedy robisz 'var = expr', nie definiujesz' var' do niezainicjowania. Definiujesz var, aby był wartością wyrażenia "expr". Chodzi o to, że podczas gdy 'expr' ocenia,' var' jest niezainicjalizowane, co oznacza, że ​​wszelkie odniesienia do 'var' wewnątrz' wyr 'będą miały wartość zero. – sepp2k

+1

@ sepp2k: Są to dwa bardzo osobne kroki. Definicja zmiennej występuje w parserze, inicjalizacja w tłumaczu. W zależności od implementacji Ruby między tymi zdarzeniami może być znaczący czas. Na przykład w BlueRuby kod źródłowy Ruby jest przetwarzany na BRIL (BlueRuby Intermediate Language), który następnie zapisywany jest w bazie danych, kiedy * instalujesz * program w języku Ruby. Może to być * lata *, dopóki ktoś faktycznie * nie uruchomi * programu. Cały czas zmienna została zdefiniowana, ale nie zainicjowana. Te dwie rzeczy mogą się nawet zdarzyć na różnych maszynach. –

1

Kiedy robisz a || 1, prosisz go szukać wartości a co jest niezdefiniowane.

Kiedy robisz a = a || 1, prosisz go, aby szukał wartości przypisania a do a, co nie wydaje się powodować błędu.

Tak więc, choć dziwne, nie uważam tego za niekonsekwentne.

+0

To jest złe.'a = a || 1' nie parsuje jako '(a = a) || 1'. Parsuje jako 'a = (a || 1)', więc wynik "przypisania a do" nie ma z tym nic wspólnego, ponieważ a nigdy nie jest przypisane do a. – sepp2k

+0

@ sepp2k: Nie próbowałem sugerować, że w ogóle. Przepraszam, jeśli cię pomyliłem. Uważam, że Jörg wyjaśnił to dokładniej. – Cetra

0

Czy to masz na myśli?

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

Może być łatwiej zadeklarować wartość z 1 jako domyślną.

Powiązane problemy