8

sono incappato in un strano comportamento in Ruby per quanto riguarda definizione della variabile (e ho perso una scatola di ciambelle sulla strada):Rubino definizione della variabile

irb(main):001:0> if false 
irb(main):002:1> a = 1 
irb(main):003:1> end 
=> nil 
irb(main):005:0> a.nil? 
=> true 
irb(main):006:0> b.nil? 
NameError: undefined local variable or method `b' for main:Object 
    from (irb):6 
    from /Users/jlh/.rbenv/versions/2.1.5/bin/irb:11:in `<main>' 

Perché non a.nil? sta gettando undefined local variable? Date un'occhiata a Python, per esempio (solo voluto confrontarlo con un linguaggio interpretato):

>>> if False: 
...  a = 1 
... 
>>> print a 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'a' is not defined 

In un linguaggio compilato questa non sarebbe nemmeno la compilazione.

  • Ciò significa che Ruby mantiene un riferimento a tale variabile anche se non ha attraversato quel pezzo di codice?
  • Se sì, quanto sono profondi i ifs/else presi in considerazione per la definizione delle variabili?

Realmente non posso credere che questo sia il comportamento previsto in rubino. E non è specifico per IRB, ma eseguirlo in un blocco di codice ruby ​​/ rails dà lo stesso risultato.

+3

È perché Ruby imposta gli ambiti variabili locali in fase di analisi. 'a' è usato come variabile locale in questo ambito, quindi Ruby lo considera come variabile locale (anche se non è mai stato inizializzato). –

+0

@MarekLipka interessante. Ho sentito che potrebbe essere parsing related, ecco perché ho usato il tag AST per questa domanda – jlhonora

+4

Leggilo da http://ruby-doc.org/core-2.1.2/doc/syntax/assignment_rdoc.html#label-Local+ Variabili + e + Metodi –

risposta

3

In Ruby, esiste un'ambiguità tra il riferimento a una variabile locale e un messaggio inviato al ricevitore implicito senza un elenco di argomenti. Ciò significa che

foo 

possibile media "dereference una variabile locale" o "invia messaggio foo-self senza argomenti", vale a dire che potrebbe essere sia equivalente a

binding.local_variable_get(:foo) 

o

self.foo() 
# or 
public_send(:foo) 

Questa ambiguità viene risolta al tempo di analisi . Quando il parser incontra un incarico a foo, da quel momento in poi, considera foo come una variabile locale, indipendentemente dal fatto che l'assegnazione venga effettivamente eseguita. (Questo è qualcosa che il parser non è in grado di determinare in modo statico, dopo tutto. Basta pensare a if rand > 0.5 then foo = 42 end.)

In un linguaggio compilato questa non sarebbe nemmeno la compilazione.

Non esiste una lingua compilata. La compilazione e l'interpretazione sono tratti del compilatore o interprete (duh!) Non della lingua. Le lingue non sono né compilate né interpretate. Loro solo sono.

Ogni lingua può essere implementata con un compilatore e ogni lingua può essere implementata con un interprete. La maggior parte delle lingue ha implementazioni compilate e interpretate (ad esempio C ha GCC e Clang, che sono compilatori e Cint e Cling, che sono interpreti, Haskell ha GHC, che è un compilatore, e Hugs, che è un interprete).

Molte implementazioni di linguaggio moderno hanno entrambi nella stessa implementazione, sia in fasi diverse (es. YARV e MRuby compilano Ruby sourcecode al bytecode interno, e quindi interpretano tale codice), o in un motore in modalità mista (ad esempio la HotSpot JVM entrambi interpretano e compila il bytecode JVM, a seconda di quale ha più senso), o entrambi (ad es. Rubinius compila il codice sorgente Ruby in codice bytecode in prima fase, e poi compila il codice bytecode in codice nativo e lo interpreta, a seconda di cosa ha più senso).

Infatti, tutti le implementazioni di Ruby attualmente esistenti sono compilate: YARV e MRuby compilare ai propri formati bytecode interne, Rubinius, MacRuby, MagLev e Topaz compilare ai propri formati bytecode interne, quindi compilare che a nativo codice, JRuby compila in bytecode JVM (che la JVM può o meno compilare ulteriormente), IronRuby compila in bytecode CIL (che il VES può o non può compilare ulteriormente).

Il fatto che Ruby si comporti in questo modo è perché le specifiche del linguaggio dicono così. Non perché Ruby è "interpretato", perché, in realtà, non lo è. L'unica implementazione puramente interpretata di Ruby era la risonanza magnetica e le prime versioni di JRuby, ed entrambi sono stati ritirati da tempo.

+0

Buona chiamata sull'argomento della compilazione, _programmazione delle lingue solo sono_ è una frase che probabilmente non dimenticherò mai – jlhonora

+0

Non posso prendermene il merito, l'ho sentito in un'intervista con [Shriram Krishnamurthi] (http: //cs.brown.edu/~sk/), credo che fosse [questo] (http://channel9.msdn.com/Shows/Going+Deep/Expert-to-Expert-Web-Programming-with- Flapjax). –

1

Potrei sbagliarmi, ma Ruby definisce gli ambiti per le variabili. Hai l'ambito globale, che è $

Quindi hai l'ambito locale del tuo script in esecuzione, che è quello che hai dimostrato nella domanda. È possibile definire la variabile all'interno di un metodo, ma sarà comunque disponibile nell'ambito locale dello script in esecuzione.

fonte: http://ruby-doc.org//docs/ruby-doc-bundle/UsersGuide/rg/localvars.html

Qui è illustrato che le variabili locali non hanno il valore iniziale pari a zero, ma una volta definito prendere qualsiasi valore che hanno, a prescindere dalla fase di definizione.

2.1.5 :001 > p 1 
1 
=> 1 
2.1.5 :002 > p a 
NameError: undefined local variable or method `a' for main:Object 
    from (irb):2 
    from /Users/deh0002a/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>' 
2.1.5 :003 > if false 
2.1.5 :004?> a = 2 
2.1.5 :005?> else 
2.1.5 :006 >  a = 3 
2.1.5 :007?> end 
=> 3 
2.1.5 :008 > p a 
3 
=> 3 
2.1.5 :009 > p$a 
nil 
=> nil 
2.1.5 :010 > p @a 
nil 
=> nil 
2.1.5 :011 > 

La differenza è di nuovo con le variabili globali e di istanza. Anche quando non sono stati definiti, assumono automaticamente il valore di zero.