2010-01-20 1 views
11

Sto solo iniziando con Ruby e personalmente trovo che quanto segue sia una violazione del "principio di minima sorpresa". E cioè, citando da the documentation, che uniq! "rimuove gli elementi duplicati da sé. Restituisce nullo se non vengono apportate modifiche (ovvero, non vengono trovati duplicati)."Perché uniq! return nil se non ci sono duplicati

Qualcuno può spiegare questo, che mi sembra del tutto contro-intuitivo? Ciò significa che piuttosto che essere in grado di scrivere una riga di codice di seguito aggiungendo .uniq! per terminare la prima linea, io invece devo scrivere le seguenti due righe:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
    hooks = hooks.uniq 

O mi manca qualcosa, un modo migliore?

EDIT:

capisco che uniq! modifica il suo operando. Ecco il problema illustrato meglio spero:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
    puts hooks.length #50 
    puts hooks.uniq!.length #undefined method `length' for nil:NilClass 

Io sostengo che il modo uniq! i lavori lo rendono completamente privo di senso e inutile. Certo nel mio caso, come ho sottolineato, potrei semplicemente aggiungere .uniq alla prima riga. Comunque più avanti nello stesso programma spingo gli elementi su un altro array all'interno di un loop. Poi, sotto il loop, mi piacerebbe "de-dupe" l'array, ma non oso scrivere "hooks_tested.uniq!" perché potrebbe restituire zero; invece io must scrittura hooks_tested = hooks_tested.uniq

effetti mi sostengono questa è una mis-feature particolarmente egregio dal fatto che è un principio ben noto che, quando escogitare un metodo che restituisce un array, si dovrebbe sempre almeno restituisce un array vuoto, anziché zero

+3

le POLS ha un significato specifico con Ruby: ". non sorprende Matz (creatore di Ruby) –

+0

Inutile per lo scopo (errato) a cui si sta cercando di metterlo, concordato Utile se. stai provando a fare qualcosa che - per esempio - verifica il risultato di "uniq!". Probabilmente sarebbe stato quello che Matz aveva in mente. ;-) –

+0

Sono d'accordo. questo è molto PHP-esque (casi eccezionali casuali, incongruenti). tutti gli altri casi, incluso 'uniq' restituisce quello originale. Quando 'uniq' non trova duplicati, restituisce l'array stesso. seriamente wtf. – ahnbizcad

risposta

10

Questo perché uniq! modifica self e se uniq! sarebbe restituire un valore che non sarebbe in grado di sapere se un cambiamento realmente accaduto in originale oggetto.

var = %w(green green yellow) 
if var.uniq! 
    # the array contained duplicate entries 
else 
    # nothing changed 
end 

Nel codice si può semplicemente scrivere

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
hooks.uniq! 
# here hooks is already changed 

Se è necessario restituire il valore di gancio, forse perché è l'ultima dichiarazione metodo di fare solo

def method 
    hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
    hooks.uniq 
end 

o in altro modo

def method 
    hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
    hooks.uniq! 
    hooks 
end 
+0

I valori di ritorno dai metodi "esclamativo" sono spesso arbitrari come questo perché modificano l'oggetto sul posto. Ciò che è sfortunato in questo caso è parte del disordine semantico che crea, come illustrato dal tuo esempio: se uniq! allora ... in realtà non è unico. – tadman

2

È possibile aggiungere uniq (nessun punto esclamativo alla fine) alla fine della prima riga.

O, se ti ostini a usare uniq!, utilizzare

(hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/)).uniq! 
+0

sì, credo che in questo caso io possa essere –

5

Il punto esclamativo su uniq! indica che esso modifica l'array invece di restituire una nuova. Si dovrebbe fare questo:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).uniq 

o questo

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
hooks.uniq! 
puts hooks.length 
+1

Tipicamente, se usi la forma di un metodo, sarà l'unico metodo che agisce su questo oggetto particolare (cioè non in una catena). Questo è in parte per eliminare dichiarazioni ambigue come 'hooks = hooks.uniq! .sort' o' hooks.uniq! .sort! '(Dove puoi avere ciò che equivale a più assegnazioni alla stessa variabile nella stessa espressione) o assegnamenti a valori intermedi temporanei come 'hooks.uniq.sort!'. 'Array.uniq!' Restituisce anche 'nil' invece di' [] 'in modo da poter fare cose come' if (hooks.uniq!) 'Per modificare l'array ed eseguire un'azione speciale se qualcosa è stato modificato. – bta

+0

Sei secondo suggerimento risultati in "lunghezza' non definita del metodo per nil: NilClass "se non contiene duplicati - questo è esattamente il comportamento inatteso a cui sto cercando di attirare l'attenzione, ma a quanto pare sta cadendo in modo illogico - er, non udenti –

+0

Ok, scusa. Se il secondo esempio non funziona, c'è qualcos'altro che succede qui. Quale versione di Ruby stai usando? – mckeed

0

Questa non è una risposta al perché, ma piuttosto una soluzione alternativa.

Dal uniq non restituisce nil, io uso uniq e assegnare il risultato ad una nuova variabile invece di utilizzare la versione di botto

original = [1,2,3,4] 
new = original.uniq 

#=> new is [1,2,3,4] 
#=> ... rather than nil 

Avere una nuova variabile è un piccolo prezzo da pagare. E 'sicuro come l'inferno battiti fare se i controlli, con le chiamate complesse ripetute uniq! e uniq e controllo per nil

1

Da Ruby 1.9, Object#tap è disponibile:

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap do |hooks| 
    hooks.uniq! 
end 
puts hooks.length 

E forse più succintamente (h/t @Aetherus):

hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/).tap(&:uniq!) 
puts hooks.length