2009-03-04 9 views
5

Qualcuno potrebbe spiegare questo pezzo di codice Ruby:codice Ruby ha spiegato

def add_spec_path_to(args) # :nodoc: 
    args << {} unless Hash === args.last 
    args.last[:spec_path] ||= caller(0)[2] 
end 

ho visto l'operatore << utilizzato per le stringhe concatenazione o come operatore binario in altre lingue, ma qualcuno potrebbe spiegare in questo contesto. È in qualche modo che accoda un lamda vuoto su args o sono totalmente in errore?

posso anche vederlo utilizzato in questo modo:

before_parts(*args) << block 

È il Hash una parola chiave?

Non sono sicuro di cosa stia dicendo l'operatore ||=.

Sono ugualmente all'oscuro di ciò che è caller(0)[2].

risposta

11

|| = è un idioma di Ruby comune: assegna il valore solo se non è già impostato. L'effetto è lo stesso codice come

if some_variable == nil 
    some_variable = some_value 
end 

o

some_variable= some_value unless some_variable 

===, quando non ignorata, confronta due oggetti per l'identità. Nel caso di Hash === args.last, Hash (che è un oggetto di tipo Classe) verifica se corrisponde alla classe dell'ultimo elemento nella matrice di args. Il codice sta facendo uso del fatto evidente che l'implementazione di Class # === impone un controllo sulla classe dell'oggetto confrontato.

Non funziona viceversa, ad esempio:

a = [{}] 
Hash === a.last #=> true 
a.last === Hash #=> false 

Gli argomenti trascinamento ad un metodo possono essere forniti come il contenuto di un hash senza la necessità di fornire il {}

Così puoi fare questo:

def hello(arg1, arg2, arg3) 
    puts [arg1.class, arg2.class, arg3.class].join(',') 
end 

hello 1,2,3 #=> Fixnum,Fixnum,Fixnum 
hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash 

Viene spesso utilizzato per fornire alla funzione un elenco di parametri opzionali di lunghezza variabile.

Sei sicuro di aver trascritto il codice originale con precisione, btw? Per ottenere un array di argomenti, normalmente si aggiunge un * all'argomento come dichiarato, altrimenti gli argomenti dovrebbero essere inseriti come una matrice, piuttosto che sconfiggere l'oggetto.

def add_spec_path_to(*args)    # now args is an array 
    args << {} unless Hash === args.last # if trailing arguments cannot be 
             # interpreted as a Hash, add an empty 
             # Hash here so that following code will 
             # not fail 
    args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not 
             # already set 
end 

EDIT: Espandendo ulteriormente sulla cosa * args, provate questo:

def x(*args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

x 1,2,:a=>5,:b=>6 
1,2,a5b6 
Fixnum,Fixnum,Hash 

... usando args * provoca args da presentare al metodo come un array. Se io non uso il *, in questo modo, per esempio:

def y(args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

... poi args deve essere un array prima che io chiamo il metodo, o vado a prendere un "ArgumentError: numero sbagliato di argomenti "per tutto tranne una cosa passata. Quindi deve apparire così:

y [1,2,{:c=>3,:d=>4}] 

... con l'hash creato in modo esplicito con {}. Ed è brutto.

Tutto quanto sopra testato con risonanza magnetica 1.8.6, btw.

+0

n, passando in un array senso, poiché args non viene restituito, così altrimenti un Hash allegata sarebbe perso. – rampion

+0

Non sono sicuro di aver capito: chiameresti il ​​metodo in questo modo: some_method ([arg1, arg2, arg3], options_hash)? È orribile: vedi la risposta sopra ... –

+0

Grande spiegazione, Mike. –

14

presumo che args sia un Array.

Hash è il nome di una classe - la prima linea spinge un hash vuoto {} sul argsmeno l'ultimo elemento di args è già un Hash (l'operatore === per prove classi se un oggetto è di una certa classe) .

L'operatore ||= è simile all'operatore +=: è più o meno equivalente a:

args.last[:spec_path] = args.last[:spec_path] || caller(0)[2] 

Così, sarà impostato args.last[:spec_path] se e solo se è attualmente impostata.

Il metodo caller restituisce informazioni sul metodo di chiamata.

1

In un po 'più corta:

def add_spec_path_to(args) # :nodoc: 

... 

# Append an empty hash to args UNLESS the last arg is a hash.. in which case do nothing 
args << {} unless Hash === args.last # so we need a hash. If it is not there, make an empty one and put it there. 

... 

#if args.last[:spec_path] equals nil or false, set it to caller(0)[2]... 

#so inside that hash from the first part, if :spec_path is not there, create it by using caller(0)[2]. 

args.last[:spec_path] ||= caller(0)[2] 

... 

end