Betrachten Sie das einfache Problem, eine veränderbare Karte mit Spur des Auftretens/Zählungen zu halten, dh mit:Zugriff/initialisieren und zu aktualisieren Werte in einer wandelbaren Karte
val counts = collection.mutable.Map[SomeKeyType, Int]()
Mein aktueller Ansatz einen Zähler zu inkrementieren ist:
counts(key) = counts.getOrElse(key, 0) + 1
// or equivalently
counts.update(key, counts.getOrElse(key, 0) + 1)
Das fühlt sich irgendwie etwas ungeschickt an, weil ich den Schlüssel zweimal angeben muss. In Bezug auf die Leistung würde ich auch erwarten, dass key
zweimal in der Karte liegen muss, was ich gerne vermeiden würde. Interessanterweise würde dieses Zugriffs- und Aktualisierungsproblem nicht auftreten, wenn Int
einen Mechanismus zur Verfügung stellen würde, um sich selbst zu modifizieren. Der Wechsel von Int
zu einer Counter
Klasse, die eine increment
Funktion würde zum Beispiel erlaubt es:
// not possible with Int
counts.getOrElseUpdate(key, 0) += 1
// but with a modifiable counter
counts.getOrElseUpdate(key, new Counter).increment
Irgendwie erwarte ich immer die folgende Funktionalität mit einer wandelbaren Karte haben (etwas ähnlich transform
aber ohne eine neue Kollektion Rückkehr und auf einem bestimmten Schlüssel mit einem Standardwert):
// fictitious use
counts.updateOrElse(key, 0, _ + 1)
// or alternatively
counts.getOrElseUpdate(key, 0).modify(_ + 1)
aber soweit ich sehen kann, eine solche Funktionalität nicht vorhanden ist. Wäre es nicht sinnvoll (Leistung und Syntax), eine solche f: A => A
In-Place-Modifikationsmöglichkeit zu haben? Wahrscheinlich verpasse ich hier nur etwas ... Ich denke, es muss eine bessere Lösung für dieses Problem geben, die eine solche Funktionalität überflüssig macht.
Update:
ich geklärt haben sollte, dass ich bin mir dessen bewusst withDefaultValue
aber das Problem bleibt das gleiche: Durchführung von zwei Lookups noch zweimal so langsam als ein, egal ob es sich um eine O (1) Operation oder nicht. Ehrlich gesagt, würde ich in vielen Situationen mehr als glücklich sein, eine Beschleunigung von Faktor 2 zu erreichen. Und natürlich kann die Konstruktion des Modifikationsverschlusses oft außerhalb der Schleife bewegt werden, so dass dies im Vergleich zu einem Lauf kein großes Problem ist Operation unnötig zweimal.
Key-Lookup in einer Standard-Map sollte normalerweise O (1) sein, also gibt es keine große Strafe, wenn man zweimal nachschaut - wahrscheinlich weniger, als die Construction für den Übergang der Funktion in updateOrElse. – Impredicative
@Impredicative: Das ist ein guter Punkt in diesem Beispiel. Aber die Funktionalität in der Eigenschaft selbst macht davon keine Annahme. Zum Beispiel wird "Map" auch durch "TreeMap" und "ListMap" implementiert, die O (log N) bzw. O (N) sind. Ohne eine O (1) -Annahme zu machen, wäre daher eine Modifizierung in situ im Allgemeinen immer noch wünschenswert. – bluenote10
Ich bin bei dir, bluenote10 - Ich war mir sicher, dass da etwas wie map.update (key, initValue) {} wäre, weil es wesentlich besser ist * und * sauberer ist. Wenn Leistung nicht wichtig wäre, würden wir vermutlich keine veränderbare Map verwenden. Und wie in einem anderen Kommentar erwähnt, ist '(_ + 1)' * keine * Schließung, da es nicht über irgendwelche freien Variablen schließt - es gibt nichts zu konstruieren. –
AmigoNico