2014-11-09 7 views
7

stavo scrivendo un codice come segue:Come usare successivo con iniettare in Ruby

[1,2,3,4,5].inject([]) do |res, a| 
    res << a*a and next if a == 2 
    res << a 
end 

Dà seguente errore:

NoMethodError: undefined method `<<' for nil:NilClass 

Come entro il prossimo rende res variabile come nil, come aggirare questo problema?

Ho provato vari modi ma non sono riuscito a lavorare con Ruby, so che questo frammento che ho fornito può essere fatto senza il prossimo (a == 4 ? res << a*a : res << a), ma nel mio caso di utilizzo effettivo ho una logica complessa e posso ' Sia fatto semplicemente

risposta

9

Sostituire la

res << a*a and next if a == 2 

con

next res << a*a if a == 2 

Ora, funzionerà.

Esempio: -

#!/usr/bin/env ruby 

ar = [1,2,3,4,5].inject([]) do |res, a| 
    next res << a*a if a == 2 
    res << a 
end 

p ar 
# >> [1, 4, 3, 4, 5] 

Read the documentation of next

next can take a value, which will be the value returned for the current iteration of the block....

+0

Grazie @Arup, questo non sapevo, come trovare tali dettagli? – Saurabh

+0

@saurabh Aggiornato .... –

+0

ok, stavo controllando la documentazione di iniettare, mio ​​male :) – Saurabh

2

Ugh. Non utilizzare un condizionale finale per questo. Invece è fatto facilmente usando uno standard if/else:

[1,2,3,4,5].inject([]) do |res, a| 
    if a == 2 
    res << a*a 
    else 
    res << a 
    end 
end 
# => [1, 4, 3, 4, 5] 

Ogni volta che ti senti come se stessi codifica se stessi in un angolo, non cercare una via d'uscita, invece, eseguire il backup e guardare a ciò che stai cercando di realizzare per vedere se c'è un modo più diretto lì.

Probabilmente avrei modificato ulteriormente il codice, per maggiore leggibilità. mantenimento a lungo termine si basa sulla comprensione rapidamente ciò che sta succedendo, e il codice che è contorto o meno evidente può prendere il suo pedaggio in seguito:

[1,2,3,4,5].inject([]) do |res, a| 
    if a == 2 
    res << a*a 
    else 
    res << a 
    end 

    res # return it for clarity in what the block is returning 
end 
# => [1, 4, 3, 4, 5] 

inject è simile a each_with_object, solo che si basa su l'accumulatore di essere restituito alla fine del blocco, motivo per cui aggiungerei lo res alla fine del blocco per chiarezza, a causa del blocco if. Il passaggio a each_with_object rimuove che il richiamo al valore di ritorno del blocco, permettendo il seguente codice per essere più logicamente chiaro:

[1,2,3,4,5].each_with_object([]) do |a, ary| 
    if a == 2 
    ary << a*a 
    else 
    ary << a 
    end 
end 
# => [1, 4, 3, 4, 5] 

Naturalmente, a quel punto, il tutto può essere ridotto ulteriormente, e potrebbe trarre vantaggio della versione ternario utilizzando:

[1,2,3,4,5].each_with_object([]) do |a, ary| 
    a2 = (a == 2) ? a * a : a 
    ary << a2 
end 
# => [1, 4, 3, 4, 5] 

Quale dei due precedenti sono più leggibile è in qualche modo alla persona di codifica e la persona responsabile per il mantenimento di esso. Mi sposterò verso la versione non ternaria perché è più facilmente estesa/estesa e non ha il rumore di linea della catena ternaria ?:.


Da quando è stato chiesto nei commenti, map riduce po 'di rumore, ed è come dovremmo trasformare un array:

[1,2,3,4,5].map { |a| 
    (a == 2) ? a * a : a 
} 

Questo è testato ma sembra corretto.

+0

Sono assolutamente d'accordo. Bella risposta –

+1

Mi chiedo perché non hai citato 'map'? –

+0

@PatriceGahide La domanda era probabilmente, perché il mio 'next' non funziona? :-) Non come scriverlo in modo migliore. –

Problemi correlati