[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Sto guardando questo codice ma il mio cervello non sta registrando come il numero 10 può diventare il risultato. Qualcuno potrebbe spiegare cosa sta succedendo qui?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Sto guardando questo codice ma il mio cervello non sta registrando come il numero 10 può diventare il risultato. Qualcuno potrebbe spiegare cosa sta succedendo qui?
Risposte:
Puoi considerare il primo argomento del blocco come un accumulatore: il risultato di ogni corsa del blocco viene memorizzato nell'accumulatore e quindi passato alla successiva esecuzione del blocco. Nel caso del codice mostrato sopra, si sta impostando l'accumulatore per impostazione predefinita, risultato, su 0. Ogni esecuzione del blocco aggiunge il numero dato al totale corrente e quindi memorizza il risultato nell'accumulatore. La prossima chiamata di blocco ha questo nuovo valore, lo aggiunge, lo memorizza di nuovo e si ripete.
Alla fine del processo, iniettare restituisce l'accumulatore, che in questo caso è la somma di tutti i valori nell'array o 10.
Ecco un altro semplice esempio per creare un hash da una matrice di oggetti, codificato dalla loro rappresentazione in formato stringa:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
In questo caso, stiamo impostando il nostro accumulatore su un hash vuoto, quindi lo popoliamo ogni volta che il blocco viene eseguito. Si noti che è necessario restituire l'hash come ultima riga del blocco, poiché il risultato del blocco verrà memorizzato nuovamente nell'accumulatore.
result + explanation
è sia la trasformazione in accumulatore che il valore di ritorno. È l'ultima riga nel blocco che lo rende un ritorno implicito.
inject
prende un valore per iniziare ( 0
nel tuo esempio) e un blocco, e lo esegue una volta per ogni elemento dell'elenco.
result + element
).Il modo più semplice per spiegare questo può essere quello di mostrare come funziona ogni passaggio, per esempio; questo è un insieme immaginario di passaggi che mostrano come questo risultato potrebbe essere valutato:
[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10
La sintassi per il metodo di iniezione è la seguente:
inject (value_initial) { |result_memo, object| block }
Risolviamo l'esempio sopra cioè
[1, 2, 3, 4].inject(0) { |result, element| result + element }
che dà il 10 come output.
Quindi, prima di iniziare vediamo quali sono i valori memorizzati in ciascuna variabile:
risultato = 0 Lo zero proviene dall'iniezione (valore) che è 0
element = 1 È il primo elemento dell'array.
Okey !!! Quindi, iniziamo a capire l'esempio sopra
Passo 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Passo 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Step: 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Step: 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Passaggio: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Qui i valori in grassetto corsivo sono elementi recuperati dall'array e i valori semplicemente in grassetto sono i valori risultanti.
Spero che tu capisca il funzionamento del #inject
metodo di #ruby
.
Il codice scorre i quattro elementi all'interno dell'array e aggiunge il risultato precedente all'elemento corrente:
Quello che hanno detto, ma nota anche che non è sempre necessario fornire un "valore iniziale":
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
equivale a
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Provalo, aspetterò.
Quando non viene passato alcun argomento da iniettare, i primi due elementi vengono passati alla prima iterazione. Nell'esempio sopra, il risultato è 1 e l'elemento è 2 la prima volta, quindi viene effettuata una chiamata in meno al blocco.
Il numero che inserisci all'interno di () di inject rappresenta un punto iniziale, potrebbe essere 0 o 1000. All'interno dei tubi hai due segnaposti | x, y |. x = qualunque numero tu abbia mai avuto all'interno di .inject ('x'), e il secondo rappresenta ogni iterazione del tuo oggetto.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Inject applica il blocco
result + element
a ciascun elemento dell'array. Per l'elemento successivo ("elemento"), il valore restituito dal blocco è "risultato". Il modo in cui l'hai chiamato (con un parametro), "risultato" inizia con il valore di quel parametro. Quindi l'effetto sta sommando gli elementi.
TLDR; inject
differisce map
in un modo importante: inject
restituisce il valore dell'ultima esecuzione del blocco mentre map
restituisce l'array su cui è ripetuto.
Oltre a ciò, il valore di ogni esecuzione di blocco è passato all'esecuzione successiva tramite il primo parametro ( result
in questo caso) e puoi inizializzare quel valore (la (0)
parte).
L'esempio sopra potrebbe essere scritto usando in map
questo modo:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
Stesso effetto ma inject
qui è più conciso.
Troverai spesso un'assegnazione nel map
blocco, mentre una valutazione avviene nel inject
blocco.
Il metodo scelto dipende dall'ambito desiderato result
. Quando non usarlo sarebbe qualcosa del genere:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
Potresti essere come tutti "Senti, ho appena combinato tutto in una riga", ma hai anche temporaneamente allocato la memoria x
come variabile scratch che non era necessaria dal momento che dovevi già result
lavorare.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
è equivalente al seguente:
def my_function(r, e)
r+e
end
a = [1, 2, 3, 4]
result = 0
a.each do |value|
result = my_function(result, value)
end
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
In parole povere, stai attraversando (ripetendo) questo array ( [1,2,3,4]
). Esaminerai questo array 4 volte, perché ci sono 4 elementi (1, 2, 3 e 4). Il metodo inject ha 1 argomento (il numero 0) e aggiungerai quell'argomento al 1 ° elemento (0 + 1. Questo equivale a 1). 1 viene salvato nel "risultato". Quindi aggiungi quel risultato (che è 1) all'elemento successivo (1 + 2. Questo è 3). Questo verrà ora salvato come risultato. Continua: 3 + 3 è uguale a 6. E infine, 6 + 4 è uguale a 10.
Questo codice non consente di non passare un valore iniziale, ma può aiutare a spiegare cosa sta succedendo.
def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end
incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10
Inizia qui e rivedi tutti i metodi che richiedono blocchi. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
È il blocco che ti confonde o perché hai un valore nel metodo? Buona domanda però. Qual è il metodo dell'operatore lì?
result.+
Come inizia?
#inject(0)
Possiamo fare questo?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
funziona?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
Vedi, mi sto basando sull'idea che somma semplicemente tutti gli elementi dell'array e produce un numero nel memo che vedi nei documenti.
Puoi sempre farlo
[1, 2, 3, 4].each { |element| p element }
per visualizzare l'enumerabile dell'array. Questa è l'idea di base.
È solo che l'iniezione o la riduzione ti danno un memo o un accumulatore che viene inviato.
Potremmo provare a ottenere un risultato
[1, 2, 3, 4].each { |result = 0, element| result + element }
ma non torna nulla, quindi questo si comporta come prima
[1, 2, 3, 4].each { |result = 0, element| p result + element }
nel blocco ispettore elemento.
Questa è una spiegazione semplice e abbastanza facile da capire:
Dimentica il "valore iniziale" in quanto è un po 'confuso all'inizio.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
Puoi capire quanto sopra come: sto iniettando una "macchina addizionatrice" tra 1,2,3,4. Significa che è 1 ♫ 2 ♫ 3 ♫ 4 e ♫ è una macchina addizionatrice, quindi è uguale a 1 + 2 + 3 + 4, ed è 10.
Puoi effettivamente iniettare una +
tra di loro:
> [1,2,3,4].inject(:+)
=> 10
ed è come, iniettare +
a tra 1,2,3,4, rendendolo 1 + 2 + 3 + 4 ed è 10. È :+
il modo di Ruby di specificare +
nella forma di un simbolo.
Questo è abbastanza facile da capire e intuitivo. E se vuoi analizzare come funziona passo dopo passo, è come: prendere 1 e 2, e ora aggiungerli, e quando hai un risultato, memorizzalo prima (che è 3), e ora, poi viene memorizzato valore 3 e l'elemento dell'array 3 che attraversa il processo a + b, che è 6, e ora memorizza questo valore, e ora 6 e 4 passano attraverso il processo a + b, ed è 10. Stai essenzialmente facendo
((1 + 2) + 3) + 4
ed è 10. Il "valore iniziale" 0
è solo una "base" per cominciare. In molti casi, non ne hai bisogno. Immagina se hai bisogno di 1 * 2 * 3 * 4 e lo è
[1,2,3,4].inject(:*)
=> 24
ed è fatto. Non è necessario un "valore iniziale" 1
per moltiplicare il tutto con 1
.
Esiste un'altra forma di metodo .inject () che è molto utile [4,5] .inject (&: +) che sommerà tutti gli elementi dell'area
È lo stesso di questo:
[1,2,3,4].inject(:+)
=> 10