[RUBY] Holen Sie sich "2-4, 7, 9" aus [4, 7, 9, 2, 3]

Einführung

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).

Code

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

Kommentar

Ohne Vervielfältigung sortieren

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.

Katalysieren Sie die Seriennummer

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 </ ruby> von der Kante zu lecken und zwei benachbarte Elemente zu verbinden oder zu trennen ** Es ist eine Methode, die nach den Kriterien ** entscheidet und auf diese Weise einen Block erstellt. Der Rückgabewert ist jedoch Enumerator und kein Array von Katamari. Ich werde in diesem Artikel nicht erklären, was ein Enumerator ist, aber da es sich um ein Enumerable-Objekt handelt, handelt es sich bei "to_a" um ein Array von Katamari.

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.

Fassen Sie die Seriennummern mit Bindestrichen zusammen

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.

Verbinden Sie sich mit einem Komma

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.

Variation

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.

Beiseite

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

Holen Sie sich "2-4, 7, 9" aus [4, 7, 9, 2, 3]
Holen Sie sich TypeElement und TypeMirror von Class
Holen Sie sich GTFS-Daten aus Graph of OpenTrip Planner
[Kotlin] Drei Möglichkeiten, um Klasse von KClass zu bekommen
Abrufen von Anruferinformationen aus dem Stack-Trace (Java)
Ruft Spaltennamen von einer Instanz von Model ab
[Java] Tag-Informationen aus Musikdateien abrufen
Abrufen des Verlaufs vom Zabbix-Server in Java
Holen Sie sich Wettervorhersage von Open Weather Map with Rails
Abrufen des Werts von URL-Parametern mit Get Mapping ~ Local Date ~
IntelliJ ab 1
So erhalten Sie einen Heapdump aus einem Docker-Container
So erhalten Sie eine Klasse von Element in Java
Holen Sie sich Unixtime (Sekunden) von ZonedDateTime in Scala / Java
Gebietsschema abrufen
[Java] Ruft mehrere Werte von einem Rückgabewert ab
Calendar.MONTH beginnt bei 0
[Java] Ruft Werte zufällig aus einem Array ab
Holen Sie sich Enum, indem Sie sich vom Inhalt zurückziehen
So erhalten Sie eine SIMD-Optimierung für HotSpot JavaVM
◆ Rufen Sie die von Spring Boot erstellte API von React ab
[Java] KFunction von Method / Constructor in Java abrufen [Kotlin]
Test von StringBoot
[Swift5] Mit der Bibliothek 'SwiftyJSON' Array- (Einzel-) Informationen von JSON abrufen.
Wie man mit cli jdk etc. von oracle bekommt