2010-01-20 9 views
11

Ich beginne gerade mit Ruby und ich persönlich finde das Folgende eine Verletzung des "Prinzips der geringsten Überraschung". Und das heißt, zitiert aus the documentation, dass uniq! "entfernt doppelte Elemente von self. Gibt null zurück, wenn keine Änderungen vorgenommen werden (dh keine Duplikate gefunden werden)."Warum uniq! return nil, wenn es keine Duplikate gibt

Kann mir jemand das erklären, was mir völlig kontraintuitiv erscheint? Dies bedeutet, dass Sie eine Zeile Code nicht schreiben können, indem Sie .uniq! Um die erste Zeile zu beenden, muss ich stattdessen die folgenden zwei Zeilen schreiben:

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

Oder fehlt mir etwas, ein besserer Weg?

EDIT:

Ich verstehe, dass uniq! ändert seinen Operanden. Hier ist das Problem illustriert besser ich hoffe:

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 

Ich behaupte, dass der Weg uniq! Werke machen es völlig sinnlos und nutzlos. Sicher in meinem Fall, wie gesagt, ich könnte nur .uniq an die erste Zeile anhängen. Später im selben Programm schiebe ich Elemente auf ein anderes Array innerhalb einer Schleife. Dann, unter der Schleife, möchte ich das Array "ent-dupen", aber ich wage nicht, "hooks_tested.uniq" zu schreiben. weil es null zurückgeben könnte; stattdessen ich muss Schreib hooks_tested = hooks_tested.uniq

Tatsächlich behaupten ich dies ist eine besonders eklatante Fehlfunktion in der, dass es ist ein gut bekanntes Prinzip, dass bei der Ausarbeitung eines Verfahrens, das ein Array zurückgibt, sollte man immer mindestens zurückgeben ein leeres Array, anstatt Null

+3

der POL eine bestimmte Bedeutung mit Ruby hat. „hat nicht überraschen Matz (Rubys Konzept) –

+0

Nutzlos für den (falschen) Zweck, zu dem Sie versuchen, es zu setzen, stimmte Praktisch, wenn. Sie versuchen etwas zu tun, das zum Beispiel das Ergebnis von 'uniq!' testet. Das wäre wahrscheinlich das, was Matz vorhatte. ;-) –

+0

stimme ich zu. Das ist sehr PHP-esque (zufällige, inkongruente Ausnahmefälle). In allen anderen Fällen, einschließlich "uniq", wird das Original zurückgegeben. Wenn 'uniq' keine Duplikate findet, wird das Array selbst zurückgegeben. ernst wtf. – ahnbizcad

Antwort

10

Dies liegt daran, uniq!self ändert und wenn uniq! einen Wert zurückgeben würde würden Sie nicht in der Lage sein, zu wissen, ob eine Änderung aufgetreten ist tatsächlich in den ursprünglichen Objekt.

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

In Ihrem Code können Sie einfach

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

schreiben Wenn Sie den Wert von Haken vielleicht zurückkommen müssen, weil es nur

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

oder sonst

tun die letzte Methode Aussage ist
def method 
    hooks = IO.read(wt_hooks_impl_file).scan(/wt_rt_00\w{2}/) 
    hooks.uniq! 
    hooks 
end 
+0

Die Rückgabewerte von "Ausrufezeichen" -Methoden sind oft willkürlich, da sie das Objekt an Ort und Stelle modifizieren. Was in diesem Fall unglücklich ist, ist ein Teil der semantischen Verwirrung, die es erzeugt, wie in Ihrem Beispiel gezeigt: Wenn uniq! dann ... eigentlich nicht einzigartig. – tadman

2

Sie können uniq (kein Ausrufezeichen am Ende) an das Ende der ersten Zeile anhängen.

Oder, wenn Sie uniq! über die Verwendung bestehen, zeigt

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

ja ich denke, ich kann in diesem Fall –

5

Das Ausrufezeichen auf uniq! verwenden, dass es das Array modifiziert stattdessen einen neuen zurückzukehren. Sie sollten dies tun:

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

oder diesen

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

Normalerweise, wenn Sie die Bang-Form einer Methode verwenden, wird es die einzige Methode sein, die darauf reagiert bestimmtes Objekt (dh nicht in einer Kette). Dies dient teilweise dazu, mehrdeutige Anweisungen wie "hooks = hooks.uniq! .sort" oder "hooks.uniq! .sort!" (Wo Sie mehrere Zuweisungen an dieselbe Variable im selben Ausdruck haben können) oder zu eliminieren Zuweisungen an temporäre Zwischenwerte wie 'hooks.uniq.sort!'. 'Array.uniq!' Gibt auch 'nil' statt' [] 'zurück, so dass Sie Dinge wie' if (hooks.uniq!) 'Tun können, um das Array zu modifizieren und eine spezielle Aktion auszuführen, wenn etwas geändert wurde. – bta

+0

Ihr zweiter Vorschlag ergibt "undefinierte Methode' length 'für nil: NilClass "wenn der Hook keine Duplikate enthält - das ist genau das unerwartete Verhalten, auf das ich aufmerksam machen möchte, aber anscheinend fällt es auf unlogische Weise - äh, taub - Ohren –

+0

Okay, tut mir leid. Wenn das zweite Beispiel nicht funktioniert, ist hier noch etwas anderes los. Welche Version von Ruby verwenden Sie? – mckeed

0

Dies ist keine Antwort auf den Grund, sondern eher ein Workaround.

Da uniq nicht zurück nil, verwende ich uniq und das Ergebnis in eine neue Variable zuweisen, anstatt den Knall Version

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

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

eine neue Variable zu haben, ist ein kleiner Preis zu zahlen zu verwenden. Es todsicher Beats, wenn Kontrollen zu tun, mit wiederholten komplexen Anrufen uniq! und uniq und Überprüfung für nil

1

Da Ruby 1.9 Object#tap ist verfügbar:

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

Und vielleicht kurz und bündig (h/t @Aetherus):

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