2010-01-20 7 views
11

Je ne fais que commencer avec Ruby et personnellement, je trouve que ce qui suit est une violation du "principe de la moindre surprise". Et c'est, en citant the documentation, que uniq! "supprime les éléments en double de self. Retourne zéro si aucune modification n'est faite (c'est-à-dire qu'aucun doublon n'est trouvé)."Pourquoi uniq! retourner zéro s'il n'y a pas de doublons

Quelqu'un peut-il expliquer cela, ce qui me semble complètement contre-intuitif? Cela signifie que plutôt que d'être en mesure d'écrire une ligne de code ci-dessous en ajoutant .uniq! pour mettre fin à la première ligne, je dois au lieu d'écrire les deux lignes suivantes:

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

Ou suis-je manque quelque chose, une meilleure façon?

EDIT:

Je comprends que uniq! modifie son opérande. Voici le problème illustré mieux J'espère:

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 

Je soutiens que la façon uniq! les travaux le rend complètement insensé et inutile. Bien sûr dans mon cas comme indiqué, je pourrais juste ajouter .uniq à la première ligne. Cependant plus tard dans le même programme, je pousse des éléments sur un autre tableau à l'intérieur d'une boucle. Ensuite, sous la boucle, j'aimerais "dé-duper" le tableau, mais je n'ose pas écrire 'hooks_tested.uniq!' parce qu'il pourrait revenir à zéro; au lieu que je doit écrire hooks_tested = hooks_tested.uniq

En effet je soutiens cela est une mauvaise caractéristique particulièrement flagrants en ce qu'il est un principe bien connu que, lors de l'élaboration d'une méthode qui retourne un tableau, on doit toujours au moins retourner un tableau vide, plutôt que nul

+3

les POLS a une signification particulière avec Ruby:. « ne pas surprendre Matz (le créateur de Ruby) –

+0

inutile pour le (incorrect) but auquel vous essayez de le mettre, d'accord pratique si. vous essayez de faire quelque chose qui - par exemple - teste le résultat de 'uniq! '. Ce serait probablement ce que Matz avait en tête. ;-) –

+0

Je suis d'accord. c'est très PHP-esque (cas exceptionnels aléatoires, incongrus). tous les autres cas, y compris 'uniq', retournent l'original. Quand 'uniq' ne trouve aucun doublon, il renvoie le tableau lui-même. sérieusement wtf. – ahnbizcad

Répondre

10

En effet, uniq! modifie self et si uniq! renverrait une valeur que vous ne serait pas en mesure de savoir si un changement a effectivement eu lieu dans l'original objet.

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

Dans votre code, vous pouvez simplement écrire

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

Si vous avez besoin de retourner la valeur de crochet peut-être parce qu'il est la dernière déclaration de méthode faire juste

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

ou autrement

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

Les valeurs renvoyées par les méthodes d'exclamation sont souvent arbitraires de la sorte car elles modifient l'objet sur place. Ce qui est malheureux à propos de ce cas est une partie du désordre sémantique qu'il crée, comme illustré par votre exemple: if uniq! alors ... en fait pas unique. – tadman

2

Vous pouvez ajouter uniq (pas de point d'exclamation à la fin) à la fin de la première ligne.

Ou, si vous insistez sur l'utilisation uniq!, utilisez

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

ouais je suppose que je peux dans ce cas –

5

Le point d'exclamation sur uniq! indique qu'il modifie le tableau au lieu de retourner une nouvelle. Vous devriez faire ceci:

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

ou cette

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

Typiquement, si vous utilisez la bang-forme d'une méthode, il sera la seule méthode agissant sur ce objet particulier (c'est-à-dire, pas dans une chaîne). C'est en partie pour éliminer les déclarations ambiguës telles que 'hooks = hooks.uniq! .sort' ou' hooks.uniq! .sort! '(Où vous pouvez avoir ce qui équivaut à plusieurs affectations à la même variable dans la même expression) ou des affectations à des valeurs intermédiaires et temporaires telles que 'hooks.uniq.sort!'. 'Array.uniq!' Retourne aussi 'nil' au lieu de' [] 'de sorte que vous pouvez faire des choses comme' if (hooks.uniq!) 'Pour modifier le tableau et effectuer une action spéciale si quelque chose a été changé. – bta

+0

Vous êtes la deuxième suggestion résultats dans "méthode indéfinie longueur" pour nil: NilClass "si elle crochets ne contient pas de doublons - c'est précisément le comportement inattendu que j'essaie d'attirer l'attention, mais apparemment il tombe sur illogique - er, sourd - oreilles –

+0

Ok, désolé. Si le deuxième exemple ne fonctionne pas, il se passe quelque chose d'autre ici. Quelle version de Ruby utilisez-vous? – mckeed

0

Ce n'est pas une réponse à la question, mais plutôt une solution de contournement.

Depuis uniq ne retourne pas nil, j'utilise uniq et lui assigner le résultat à une nouvelle variable au lieu d'utiliser la version bang

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

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

Avoir une nouvelle variable est un petit prix à payer. Il sûr que bat l'enfer faire si les contrôles, avec des appels complexes répétés à uniq! et uniq et la vérification des nil

1

Depuis 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 

Et peut-être plus succinctement (h/t @Aetherus):

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