2013-10-25 33 views
10

Rozważmy następujący fragment z IRB świeżo rozpoczął sesję:Dziwne znaczenie || i || = w Ruby (2.0, 1.9.3, 1.7.4 jruby)

irb:01> baz   # => NameError, baz is not defined 
irb:02> baz || baz = 0 # => NameError, baz is not defined 
irb:03> baz   # => nil 

baz była niezdefiniowana zmienna i próbując ocenić wyprodukowany a NameError. Jednak w jakiś sposób po tej operacji zdefiniowano baz i ma ona wartość nil. Pozornie wartość nil została przypisana do zmiennej baz, mimo że nikt (wyraźnie) nie prosił o jej podanie. Czy jest jakiś podstawowy powód, dla którego to zachowanie jest pożądane?

Jaka jest zasada, że ​​wyjaśnia to zachowanie i innych podobnie mylące konstrukcje, takie jak te:

irb:04> true if foo   # => NameError 
irb:05> foo     # => NameError; name still undefined 
irb:06> foo = (true if foo) # => nil 
irb:07> foo     # => nil; name defined as nil 
irb:08> true || i = 0 || j = 2 # => i and j are nil; || appears nonlazy 
irb:09> raise || quux = 1  # => RuntimeError, quux is nil 
+3

Nie używasz '|| =' w żadnym z przykładów, tytuł pytania jest nieco mylący. – nzifnab

+0

Jeszcze dziwniej: '>> spam # => NameError; >> spam || = "jaja" # => "jaja"; >> spam # => "jaja" '. Niespójny. – iamnotmaynard

+0

możliwy duplikat [Confusion z operacją przypisania wewnątrz błędu \ 'if \' block] (http://stackoverflow.com/questions/15183576/confusion-with-tassing-operation-inside-the -fallacy-if -block) –

Odpowiedz

9

ja nie wiem, czy jest to pożądane, ale pochodzi ona od jak Ruby analizuje kod. Ilekroć masz kawałek kodu, który przypisuje zmienną lokalną, ta lokalna zmienna jest przypisywana nil, nawet jeśli ten fragment kodu nie jest oceniany. W linii Kod 2:

baz || baz = 0 

pierwszy baz zwrócony błąd, ponieważ zmienna taki nie został przypisany. Stąd przydział baz = 0 nie był oceniany , ale mimo to był przeanalizowany, więc w kontekście do następnej, została utworzona lokalna zmienna baz i jest inicjowana na nil.

Za pomocą drugiego fragmentu kodu foo nie jest przypisywany podczas true if foo i foo. Po tym, foo = (true if foo) ma przypisanie do foo, więc mimo że (true if foo) jest oceniane przed przypisaniem foo, błąd nie jest podnoszony w tej linii.

+1

Muszę usunąć swój komentarz po poprawieniu odpowiedzi ;-) Teraz to prawda, przypisanie do baz jest ** nie ** wykonywane, ale ilekroć Ruby ** parser ** wykrywa przypisanie, przydziela miejsce dla to i ustawia na 'nil'. –

+0

@DavidUnric Być może miałeś coś do powiedzenia mojej odpowiedzi, tak jak było wcześniej, ale teraz, nie sądzę, że twój komentarz dodaje jakieś informacje poza moją odpowiedzią. – sawa