Dieser Artikel wurde unter Berücksichtigung der folgenden Artikel verfasst. Ich habe versucht, ein einfaches Programmierproblem mit Ruby Pattern Matching - Qiita zu lösen
Der obige Artikel
"1, 5, 10-12, 15, 18-20"
Aus der Zeichenkette
[1, 5, 10, 11, 12, 15, 18, 19, 20]
Es wird beschrieben, wie Sie das Array in Ruby abrufen.
Auf der anderen Seite ist dieser Artikel das Gegenteil. Mit anderen Worten, wenn ein Array positiver Ganzzahlen angegeben wird, wird es sortiert, und dann werden die Seriennummern durch Bindestriche verbunden, um eine durch Kommas getrennte Zeichenfolge zu erstellen.
Dieser Prozess wird beispielsweise verwendet, um die Notation der Seitenzahlen für einen Buchindex zu erstellen.
Darüber hinaus ist in einer solchen Notation
Es gibt zwei mögliche Stile, aber in diesem Artikel werden wir den ersteren nehmen (auch über den letzteren schreiben).
Ich habe den Testcode auch mit test-unit geschrieben.
require "test/unit"
def gather(array)
array.uniq.sort
.chunk_while{ |a, b| a.succ == b }
.map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }
.join(", ")
end
#Unten ist der Testcode
#Wenn Sie das Skript ausführen, wird der unten beschriebene Test automatisch durchgeführt.
class TestGather < Test::Unit::TestCase
test "Zerstückelt" do
assert_equal "1, 3, 5", gather([3, 5, 1])
end
test "Es gibt Doppelarbeit" do
assert_equal "2, 4", gather([4, 2, 2, 4, 4])
end
test "Zusammenfassung" do
assert_equal "1-2", gather([2, 1])
assert_equal "1-3", gather([2, 3, 1])
assert_equal "2-4, 7, 9, 11-12",
gather([12, 11, 7, 9, 2, 4, 3])
end
end
Zunaechst
array.uniq.sort
Infolgedessen wird die Duplizierung weggelassen und eine Sortierung durchgeführt. Ich denke nicht, dass es viel Erklärung braucht. In Anbetracht der Effizienz sollte jedoch beachtet werden, dass es besser ist, zuerst "uniq" und dann "sort" [^ uniq-sort] zu verwenden.
[^ uniq-sort]: Nicht viel anders, wenn es nur wenige Duplikate gibt. Es kann jedoch doktrinär sein, sich an ".uniq.sort" zu erinnern.
Als nächstes ist das Herz dieses Codes
chunk_while{ |a, b| a.succ == b }
Teil von. Tatsächlich enthält die offizielle Referenz Enumerable # chunk_while ein Beispiel, das genau dem Code in diesem Artikel entspricht. Obwohl es getan wurde.
chunk_while
ist eine wirklich interessante Ruby-ähnliche Methode. Wer ist "chunk_while"?
Für ein Enumerable-Objekt wie ein Array ist dies eine Entscheidung, die Elemente
Zum Beispiel
[2, 6, 0, 3, 5, 8]
Nehmen wir an, es gibt eine Anordnung namens, und wir möchten diese in gerade und ungerade Kategorien unterteilen. Mit anderen Worten
[[2, 6, 0], [3, 5], [8]]
Ich möchte ein Array namens. Wichtig ist, dass nur ** benachbarte ** gerade und ungerade ** katalysiert werden. Daher wird die letzte "8" nicht mit "2, 6, 0" kombiniert, sondern ist ein Sprungpunkt.
Wie sollen wir übrigens den bedingten Ausdruck schreiben, dass die beiden ganzen Zahlen "a" und "b" "beide sind gerade oder beide sind ungerade" sind? Ein Weg ist
(a.even? && b.even?) || (a.odd? && b.odd?)
Aber Sie müssen diese Art von Ärger nicht machen. Wenn Sie feststellen, dass "der Unterschied zwischen gerade und gerade gerade ist", "der Unterschied zwischen ungerade und ungerade gerade ist" und "der Unterschied zwischen gerade und ungerade ungerade ist"
(a - b).even?
Ich weiß, dass es okay ist.
Um nun ein Array benachbarter gerader und ungerader Katamari zu erhalten, schreiben Sie wie folgt mit chunk_while
.
numbers = [2, 6, 0, 3, 5, 8]
p numbers.chunk_while{ |a, b| (a - b).even? }.to_a
# => [[2, 6, 0], [3, 5], [8]]
Wie funktioniert das "chunk_while"?
chunk_while
verwendet each
, um jedes Element vom Empfänger abzurufen.
Tun Sie dies zunächst, um die beiden Elemente zu extrahieren. In diesem Fall werden "2" und "6" abgerufen. Übergeben Sie diese beiden an den Block und bewerten Sie den Block. Dann (2 -6) .even?
Ist wahr. Zu diesem Zeitpunkt entscheidet "chunk_while": "Hmmmm, dann werde ich nicht zwischen" 2 "und" 6 "trennen."
Übergeben Sie als nächstes die bereits abgerufene "6" und die nächste abgerufene "0" an den Block. Auch hier gibt der Block true zurück, sodass die Verbindung nicht getrennt wird.
In ähnlicher Weise übergeben Sie "0" und "3" an den Block, der false zurückgibt. Dann entscheidet chunk_while
:" Okay! Schneiden Sie es hier ab! ".
Gleiches gilt weiter unten.
Es ist ähnlich wie bei "each_cons (2)", da "chunk_while" zwei benachbarte Elemente aufnimmt und ihre Positionen nacheinander verschiebt.
each_cons (2)
nimmt nur zwei Elemente auf, während chunk_while
zwei Elemente aufnimmt, um zu bestimmen, wo verbunden / getrennt werden soll.
Jetzt, da Sie wissen, wie "chunk_while" funktioniert
chunk_while{ |a, b| a.succ == b }
Lassen Sie uns überlegen.
Integer # succ
ist eine Methode, die eine Ganzzahl zurückgibt, die durch Hinzufügen von 1 zu sich selbst erhalten wird.
"a.succ == b" drückt die Bedingung aus, dass auf "a" "b" folgt.
Nächster
map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }
Ist einfach.
Ups, ein Wort davor. Da der Rückgabewert von "chunk_while" ein Enumerator-Objekt ist, ist es nicht erforderlich, es in ein Array mit "to_a" zu verwandeln, und "map" kann so wie es ist fortgesetzt werden.
Übrigens: Wenn in Bezug auf diesen "Karten" -Block jede Katamari 1 lang ist, bleibt sie unverändert, und wenn sie 2 oder mehr beträgt, sind Anfang und Ende mit einem Bindestrich verbunden.
Zum Beispiel bleibt "[7]" als "[7]" und "[2, 3, 4]" wird in "2-4" geändert. Nun, das konvertierte Array ist etwas unangenehm, da die Elemente Arrays und Strings sind. Nein, es gibt kein Problem damit (später beschrieben), aber es ist sicher, dass es ein wenig matschig sein wird.
Schließlich
join(", ")
Enden mit. Nun, dies verbindet nur die Elemente mit ",", aber um sicher zu sein, dass Sie im vorherigen Absatz gesagt haben, dass "Elemente Arrays oder Strings sein können, spielt es keine Rolle", Array # join benötigt ein genaues Verständnis.
join
gibt eine Zeichenfolge zurück, die mit einem dazwischen liegenden Argument verbunden ist, wenn alle Elemente Zeichenfolgen sind.
Wenn einige der Elemente keine Zeichenfolgen sind, werden sie zuerst in Zeichenfolgen konvertiert, bevor sie verbunden werden. Wenn es sich jedoch um Arrays handelt, werden sie mit "join" anstelle von "to_s" in Zeichen konvertiert. Zu diesem Zeitpunkt werden die gleichen Argumente wie beim ursprünglichen "Join" für "Join" verwendet.
Damit
p [1, [2, 3]].join("-") # => "1-2-3"
so werden.
Mit dem Obigen ist die Operation vollständig verstanden.
Wie ich im Abschnitt "Einführung" vorausgesagt habe, wie kann ich ihn so ändern, dass er nicht mit Bindestrichen gruppiert wird, wenn nur zwei aufeinanderfolgende Zahlen wie "4,5" vorhanden sind?
Mit anderen Worten
p gather([1, 2, 4, 5, 6]) # => "1, 2, 4-6"
Wie man es macht
Tatsächlich wird der Beispielcode für Enumerable # chunk_while gemäß diesem Stil geschrieben.
Um das zu tun
.map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }
Zu
.map{ |c| (c.size < 3) ? c : "#{c[0]}-#{c[-1]}" }
Sie können es in ändern. Aus den bereits erwähnten Spezifikationen von "join" geht hervor, dass nur diese Änderung erforderlich ist.
Es ist bedauerlich, dass Rubys ähnliche Unit-Test-Bibliothek weiterhin in Test-Unit und Minitest aufgeteilt ist.
In meinen Augen scheint die Testeinheit besser zu sein [^ tu], aber Rails hat den Minitest übernommen (eine magische Modifikation davon?). Der Minitest kann also der Welt überlegen sein. Unbekannt.
[^ tu]: Mit test-unit können Sie den Testnamen wie in diesem Artikel als Zeichenfolge angeben und datengesteuerte Tests durchführen.
e? RSpec? Nein, ich habe nicht das Gefühl, dass ich es schreiben kann, weil es mir zu unklar ist. Was ist es?
Recommended Posts