2010-01-20 16 views
11

Estoy empezando con Ruby y personalmente considero que lo siguiente es una violación del "principio de menor sorpresa". Y eso es, citando desde the documentation, ese uniq! "elimina elementos duplicados de uno mismo. Devuelve nil si no se realizan cambios (es decir, no se encuentran duplicados)".¿Por qué uniq! devuelva nil si no hay duplicados

¿Alguien puede explicar esto, lo cual me parece totalmente contrario a la intuición? Esto significa que en lugar de poder escribir una línea de código a continuación, se agrega .uniq! para poner fin a la primera línea, que en lugar tengo que escribir las dos líneas siguientes:

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

O me estoy perdiendo algo, una mejor manera?

EDIT:

entiendo que uniq! modifica su operando. Aquí está el problema ilustra mejor espero:

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 

Afirmo que el camino uniq! funciona lo hace completamente insensato e inútil. Claro que en mi caso, como señalé, podría agregar .uniq a la primera línea. Sin embargo, más adelante en el mismo programa, estoy empujando elementos hacia otra matriz dentro de un bucle. Luego, debajo del ciclo, me gustaría "quitarle el dupe" a la matriz, ¡pero no me atrevo a escribir 'hooks_tested.uniq!' porque podría devolver nada; en lugar de eso obligada escritura hooks_tested = hooks_tested.uniq

hecho I sostienen este es un mis-característica particularmente atroz en que es un principio bien conocido de que, al diseñar un método que devuelve una matriz, uno debe siempre al menos devolver una matriz vacía, en lugar de cero

+3

los Pols tiene un significado específico con Ruby:. "no sorprende a Matz (creador de Ruby) –

+0

inútil para el propósito (incorrecta) a la que usted está tratando de ponerlo, acordado práctico si. estás intentando hacer algo que, por ejemplo, prueba el resultado de 'uniq!'. Eso probablemente sería lo que Matz tenía en mente. ;-) –

+0

Estoy de acuerdo. esto es muy parecido a PHP (casos excepcionales aleatorios e incongruentes). todos los demás casos, incluido 'uniq', devuelve el original. Cuando 'uniq' no encuentra duplicados, devuelve la matriz en sí. seriamente wtf. – ahnbizcad

Respuesta

10

Esto se debe a uniq! modifica self y si uniq! podría devolver un valor que no sería capaz de saber si un cambio que realmente ocurrió en el original objeto.

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

En su código simplemente hay que escribir

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

Si necesita devolver el valor de gancho tal vez porque es la última declaración método que acabamos de hacer

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

o de otra manera

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

Los valores de retorno de los métodos de "exclamación" suelen ser arbitrarios porque modifican el objeto in situ. Lo desafortunado de este caso es el desastre semántico que crea, como lo ilustra su ejemplo: ¡si uniq! entonces ... en realidad no es único. – tadman

2

Puede agregar uniq (sin signo de admiración al final) al final de la primera línea.

O, si usted insiste en el uso de uniq!, utilice

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

sí, supongo que puedo en este caso –

5

El signo de exclamación en uniq! indica que se modifica la matriz en lugar de devolver una nueva. Usted debe hacer esto:

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

o esta

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

Normalmente, si utiliza la forma de bang de un método, será el único método que actúa en ese objeto particular (es decir, no en una cadena). Esto es en parte para eliminar afirmaciones ambiguas como 'hooks = hooks.uniq! .sort' o' hooks.uniq! .sort! '(Donde puede tener lo que equivale a múltiples asignaciones a la misma variable en la misma expresión) o asignaciones a valores temporales intermedios, como 'hooks.uniq.sort!'. 'Array.uniq!' También devuelve 'nil' en lugar de' [] 'para que pueda hacer cosas como' if (hooks.uniq!) 'Para modificar la matriz y realizar una acción especial si se cambia algo. – bta

+0

Su segunda sugerencia da como resultado "método indefinido' longitud 'para nil: NilClass "si no contiene duplicados: este es precisamente el comportamiento inesperado al que estoy tratando de llamar la atención, pero aparentemente está cayendo en lo ilógico, er, sordo - oídos –

+0

Bien, lo siento. Si el segundo ejemplo no funciona, aquí está sucediendo algo más. ¿Qué versión de Ruby estás usando? – mckeed

0

Esta no es una respuesta a por qué, sino más bien, una solución alternativa.

Desde uniq no vuelve nil, utilizo uniq y asignar el resultado a una nueva variable en lugar de utilizar la versión explosión

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

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

Tener una nueva variable es un pequeño precio a pagar. Seguro como el infierno latidos hacer si los controles, con llamadas repetidas a complejos uniq! y uniq y la comprobación de nil

1

Desde Ruby 1.9, Object#tap está disponible:

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

Y quizás más sucintamente (h/t @Aetherus):

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