Was ist der beste Name für eine nicht mutierende Add-Methode für eine unveränderliche Sammlung?


229

Entschuldigung für den Waffeltitel - wenn ich einen prägnanten Titel finden könnte, müsste ich die Frage nicht stellen.

Angenommen, ich habe einen unveränderlichen Listentyp. Es gibt eine Operation, Foo(x)die am Ende eine neue unveränderliche Liste mit dem angegebenen Argument als zusätzliches Element zurückgibt. Um eine Liste von Zeichenfolgen mit den Werten "Hallo", "unveränderlich", "Welt" zu erstellen, können Sie Folgendes schreiben:

var empty = new ImmutableList<string>();
var list1 = empty.Foo("Hello");
var list2 = list1.Foo("immutable");
var list3 = list2.Foo("word");

(Dies ist C # -Code, und ich bin am meisten an einem C # -Vorschlag interessiert, wenn Sie der Meinung sind, dass die Sprache wichtig ist. Es handelt sich nicht grundsätzlich um eine Sprachfrage, aber die Redewendungen der Sprache können wichtig sein.)

Wichtig ist, dass die vorhandenen Listen nicht durch geändert werden Foo- also empty.Countimmer noch 0 zurückgeben.

Ein anderer (idiomatischerer) Weg, um zum Endergebnis zu gelangen, wäre:

var list = new ImmutableList<string>().Foo("Hello")
                                      .Foo("immutable")
                                      .Foo("word");

Meine Frage ist: Was ist der beste Name für Foo?

EDIT 3 : Wie ich später verrate, ist der Name des Typs möglicherweise nicht der richtige ImmutableList<T>, was die Position klar macht. Stellen Sie sich stattdessen vor, TestSuitedass es unveränderlich ist, weil das gesamte Framework, zu dem es gehört, unveränderlich ist ...

(Ende der Bearbeitung 3)

Optionen, die ich mir bisher ausgedacht habe:

  • Add: häufig in .NET, impliziert jedoch eine Mutation der ursprünglichen Liste
  • Cons: Ich glaube, dies ist der normale Name in funktionalen Sprachen, aber für diejenigen ohne Erfahrung in solchen Sprachen bedeutungslos
  • Plus: Mein bisheriger Favorit, das bedeutet für mich keine Mutation . Anscheinend wird dies auch in Haskell verwendet, jedoch mit leicht unterschiedlichen Erwartungen (ein Haskell-Programmierer könnte erwarten, dass er zwei Listen zusammenfügt, anstatt der anderen Liste einen einzelnen Wert hinzuzufügen).
  • With: im Einklang mit einigen anderen unveränderlichen Konventionen, hat aber nicht ganz die gleiche "Hinzufügung" IMO.
  • And: nicht sehr beschreibend.
  • Betreiberüberlastung für +: Ich mag das wirklich nicht so sehr; Ich denke im Allgemeinen, dass Operatoren nur auf Typen niedrigerer Ebenen angewendet werden sollten. Ich bin jedoch bereit, mich überzeugen zu lassen!

Die Kriterien, die ich für die Auswahl verwende, sind:

  • Gibt den richtigen Eindruck vom Ergebnis des Methodenaufrufs (dh, es ist die ursprüngliche Liste mit einem zusätzlichen Element).
  • Macht so deutlich wie möglich, dass die vorhandene Liste nicht mutiert wird
  • Klingt vernünftig, wenn sie wie im zweiten Beispiel oben verkettet sind

Bitte fragen Sie nach weiteren Details, wenn ich mich nicht klar genug mache ...

EDIT 1: Hier ist meine Argumentation für die Bevorzugung Pluszu Add. Betrachten Sie diese beiden Codezeilen:

list.Add(foo);
list.Plus(foo);

Meiner Ansicht nach (und das ist eine persönliche Sache) ist letzteres eindeutig fehlerhaft - es ist wie das Schreiben von "x + 5;" als eine Aussage für sich. Die erste Zeile scheint in Ordnung zu sein, bis Sie sich daran erinnern, dass sie unveränderlich ist. Tatsächlich ist die Art und Weise, wie der Plus-Operator alleine seine Operanden nicht mutiert, ein weiterer Grund, warum dies Plusmein Favorit ist. Ohne die leichte Schwierigkeit der Operatorüberladung gibt es immer noch die gleichen Konnotationen, einschließlich (für mich), dass die Operanden (oder in diesem Fall das Methodenziel) nicht mutiert werden.

EDIT 2: Gründe, warum Add nicht gefällt.

Verschiedene Antworten sind effektiv: "Gehen Sie mit Hinzufügen. Das ist, was DateTimetut, und Stringhat ReplaceMethoden usw., die die Unveränderlichkeit nicht offensichtlich machen." Ich stimme zu - hier hat Vorrang. Allerdings habe ich gesehen , viele Leute nennen DateTime.Addoder String.Replaceund erwarten Mutation . Es gibt eine Menge Newsgroup-Fragen (und wahrscheinlich SO-Fragen, wenn ich mich umschaue), die mit "Sie ignorieren den Rückgabewert von String.Replace; Zeichenfolgen sind unveränderlich, eine neue Zeichenfolge wird zurückgegeben." Beantwortet werden .

Jetzt sollte ich die Frage subtil behandeln - der Typ ist möglicherweise keine unveränderliche Liste, sondern ein anderer unveränderlicher Typ. Insbesondere arbeite ich an einem Benchmarking-Framework, bei dem Sie einer Suite Tests hinzufügen und eine neue Suite erstellen. Es könnte offensichtlich sein, dass:

var list = new ImmutableList<string>();
list.Add("foo");

wird nichts bewirken , aber es wird viel düsterer, wenn Sie es ändern in:

var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);

Das sieht so aus, als ob es in Ordnung sein sollte. Während dies für mich den Fehler klarer macht:

var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);

Das bettelt nur darum:

var suite = new TestSuite<string, int>().Plus(x => x.Length);

Im Idealfall möchte ich, dass meinen Benutzern nicht mitgeteilt werden muss, dass die Testsuite unveränderlich ist. Ich möchte, dass sie in die Grube des Erfolgs fallen. Dies ist möglicherweise nicht möglich, aber ich würde es gerne versuchen.

Ich entschuldige mich für die übermäßige Vereinfachung der ursprünglichen Frage, indem ich nur über einen unveränderlichen Listentyp spreche. Nicht alle Sammlungen sind so selbsterklärend wie ImmutableList<T>:)


2
@Adam: Nein, letzteres ist eindeutig fehlerhaft. Sie sind beide wirklich Buggy (wie sie nichts mit dem Ergebnis , tun) - aber die erste nicht funktioniert schauen mir Buggy.
Jon Skeet

36
Ist ein Uncat ein Hund?
Michael Myers

7
Concat / Condog ... funktioniert bei mir! ;)
Gnovice

11
Uncat - das wäre eine Zombiekatze.
Erik Forbes

2
@Trap: Das ist scheiße, wenn es darum geht, die API flüssig zu machen.
Jon Skeet

Antworten:


130

In solchen Situationen gehe ich normalerweise mit Concat. Das bedeutet für mich normalerweise, dass ein neues Objekt erstellt wird.

var p = listA.Concat(listB);
var k = listA.Concat(item);

3
Dies ist, was ich benutze, weil es das ist, was System.Linq.Enumerable verwendet - die Entscheidung ist bereits getroffen. :) Ich kann nicht die einzige Person sein, die ihre eigene Überladung der Concat-Erweiterung in IEnumerable definiert hat, um einen einzelnen anzuhängenden Wert zu akzeptieren.
Daniel Earwicker

9
+1 Dies ist die richtige Antwort, um die Namenskonsistenz innerhalb des Frameworks aufrechtzuerhalten.
Sam Harwell

Sofern ich es nicht vermisse, verwendet System.Linq.Enumerable (und Linq im Allgemeinen) Concat () nur für "Sequenz + Sequenz", niemals für "Element + Sequenz". Das Problem, dass jemand "erwarten könnte, dass zwei Listen zusammengefügt werden, anstatt der anderen Liste einen einzelnen Wert hinzuzufügen", wurde ausdrücklich als Grund angegeben, eine der anderen ursprünglichen Optionen nicht zu verwenden.
Ssswift

119

Ich würde mich aus einem einfachen Grund für Cons entscheiden: Es bedeutet genau das, was Sie wollen.

  1. Ich bin ein großer Fan davon, genau zu sagen, was ich meine, besonders im Quellcode. Ein Neuling muss die Definition von Cons nur einmal nachschlagen, diese dann aber tausendmal lesen und verwenden. Ich finde, dass es auf lange Sicht schöner ist, mit Systemen zu arbeiten, die den allgemeinen Fall erleichtern, auch wenn die Vorlaufkosten etwas höher sind.

  2. Die Tatsache, dass es für Menschen ohne FP-Erfahrung "bedeutungslos" wäre, ist tatsächlich ein großer Vorteil. Wie Sie bereits betont haben, haben alle anderen Wörter, die Sie gefunden haben, bereits eine Bedeutung, und diese Bedeutung ist entweder geringfügig unterschiedlich oder mehrdeutig. Ein neues Konzept sollte ein neues Wort haben (oder in diesem Fall ein altes). Ich möchte lieber, dass jemand die Definition von Cons nachschlägt, als fälschlicherweise anzunehmen, dass er weiß, was Add tut.

  3. Andere Operationen, die aus funktionalen Sprachen entlehnt wurden, behalten häufig ihre ursprünglichen Namen bei, ohne dass offensichtliche Katastrophen auftreten. Ich habe keinen Anstoß gesehen, Synonyme für "map" und "redu" zu finden, die Nicht-FPern vertrauter klingen, und ich sehe auch keinen Nutzen daraus.

(Vollständige Offenlegung: Ich bin ein Lisp-Programmierer, daher weiß ich bereits, was Nachteile bedeuten.)


16
Vergessen Sie jedoch nicht, dass die Auffindbarkeit wichtig ist - wenn ich "testSuite" eingebe. und schauen Sie sich die Liste der Methoden an, ich würde gerne eine Methode sehen, die das Richtige vorschlägt. Ich werde wahrscheinlich keinen unsinnigen Namen nachschlagen, wenn es das ist, was ich will.
Jon Skeet

33
Wenn ich mich snarky fühlte, würde ich eine Methode Add machen, die einfach eine Ausnahme mit message = "Use Cons, um einer ImmutableList voranzustellen" auslöste. :-)
Ken

2
Es wird niemanden umbringen, ein paar Zeilen mit Kommentaren zu lesen, in denen erklärt wird, warum der Name so ist, wie er ist, und sie beispielsweise auf Abelson & Sussmans "Struktur und Interpretation von Computerprogrammen" zu verweisen.
John R. Strohm

16
Traditionell werden Nachteile eher am Anfang als am Ende hinzugefügt. Es handelt sich also um einen möglicherweise irreführenden Namen für die beschriebene Methode.
Walkytalky

16
Nur um dem nächsten nicht funktionierenden Programmierer einen Klick oder 3 ... en.wikipedia.org/wiki/Cons vom Wort "Konstrukt" zu ersparen , bedeutet der Ausdruck "x auf y setzen", ein neues Objekt mit (zu konstruieren) Nachteile xy)
Myster

59

Eigentlich mag ich es Andbesonders idiomatisch. Mir würde es besonders gefallen, wenn Sie eine statische schreibgeschützte Eigenschaft für die leere Liste hätten und den Konstruktor möglicherweise privat machen würden, sodass Sie immer aus der leeren Liste erstellen müssen.

var list = ImmutableList<string>.Empty.And("Hello")
                                      .And("Immutable")
                                      .And("Word");

Ich mag die leere Idee. Immer noch nicht überzeugt von Und obwohl. Es ist nur ein bisschen bla.
Jon Skeet

Es scheint eher so, als würde ich darüber nachdenken, eine Liste von Dingen in natürlicher Sprache zu erstellen. Wenn Sie es laut vorlesen, erscheint es intuitiver als Hinzufügen oder Plus. "Liste" ist die leere Liste und "Hallo" und "Unveränderlich" und "Wort". Ich würde jedoch zustimmen, dass es isoliert weniger klar ist.
Tvanfosson

52

Immer wenn ich in einem Stau mit Nomenklatur bin, habe ich die Interwebs aufgerufen.

thesaurus.com gibt dies für "add" zurück:

Definition: angrenzen, erhöhen; mach einen weiteren Kommentar

Synonyme: Anbringen, Anhängen, Anhängen, Anhängen, Erweitern, Auffrischen, Boosten, Aufbauen, Aufladen, Fortfahren, Aufrufen, Herausfinden, Ausarbeiten, Aufheizen, Wandern, Aufsteigen, Anhängen, Anhängen, Anschließen mit, einschließen, aufbocken, aufpeppen, zusammenfügen, auffüllen, parlieren, huckepack nehmen, einstecken, aufgießen, antworten, hochlaufen, weiter sagen, aufschlagen, Schneeball, Suppe aufdrehen, beschleunigen, spitzen, aufsteigen, ergänzen, versüßen, anheften, etikettieren

Ich mag den Klang von Adjoinoder einfacher Join. Das machst du doch, oder? Die Methode könnte auch für das Verbinden anderer gelten ImmutableList<>.


1
Ich mag auch 'Join', aber in den meisten Fällen, wenn Sie 2 Objekte verbinden, erhalten Sie 1. In diesem Fall erstellen Sie tatsächlich ein neues Objekt, wenn Sie 2 Objekte verbinden.
Outlaw Programmer

1
Ich kenne keine Fälle in .NET, Perl, PHP oder sogar VBScript, in denen Join eine Mutation impliziert. Das Design ist so, dass A und B sich zu C verbinden, wobei C immer eine neue Entität ist.
Spoulson

Ich mag Join und stimme thesaurus.com voll und ganz zu :) Verwenden Sie es die ganze Zeit, wenn Sie Zweifel an einem Namen haben.
Skurmedel

var suite = new TestSuite <string, int> () .Join (x => x.Length);
Sly Gryphon

3
Geh mit Piggyback, imo. Oder HookUpWith.
Chris Marasti-Georg

48

Persönlich mag ich .With (). Wenn ich das Objekt verwenden würde, wäre nach dem Lesen der Dokumentation oder der Codekommentare klar, was es tut, und es liest sich im Quellcode in Ordnung.

object.With("My new item as well");

Oder Sie fügen "Mit" hinzu .. :)

object.AlongWith("this new item");

+1. "WITH" ist ein Infix-Operator in SETL, der dasselbe tut. Wenn SETL es benutzt, muss es richtig sein :-)
finnw

1
Bah ... Wer benutzt VB noch? :) Äh .. Opps .. War das laut? heh .. Aber nein, im Ernst, deshalb habe ich das "AlongWith" in Betracht gezogen, das das VB-Problem beseitigen würde. Es gibt nur ungefähr eine Million verschiedene Wege, die er mit diesem gehen könnte ... Ich meine, sogar verrückte wie: object.Plus () oder Object.ExistingPlus () ... etc ... Es ist eine verdammt gute Frage, die er hat geschrieben jedoch ... heh ..
LarryF

33

Am Ende habe ich für alle meine unveränderlichen Sammlungen in BclExtras Add hinzugefügt . Der Grund dafür ist, dass es sich um einen leicht vorhersehbaren Namen handelt. Ich mache mir nicht so viele Sorgen darüber, dass Leute Add mit einem mutierenden Add verwechseln, da dem Namen des Typs Immutable vorangestellt ist.

Für eine Weile dachte ich über Nachteile und andere funktionale Stilnamen nach. Schließlich habe ich sie abgezinst, weil sie bei weitem nicht so bekannt sind. Sicher, funktionale Programmierer werden es verstehen, aber sie sind nicht die Mehrheit der Benutzer.

Andere Namen: Sie haben erwähnt:

  • Plus: Ich wünsche / wasche mich auf diesem. Für mich bedeutet dies nicht mehr, dass es sich um eine nicht mutierende Operation handelt, als dies bei Add der Fall ist
  • Mit: Verursacht Probleme mit VB (Wortspiel beabsichtigt)
  • Überlastung des Bedieners: Die Auffindbarkeit wäre ein Problem

Optionen, die ich in Betracht gezogen habe:

  • Concat: Strings sind unveränderlich und verwenden diese. Leider ist es nur wirklich gut, um am Ende hinzuzufügen
  • CopyAdd: Was kopieren? Die Quelle, die Liste?
  • AddToNewList: Vielleicht eine gute für List. Aber was ist mit einer Sammlung, einem Stapel, einer Warteschlange usw.

Leider scheint es kein Wort zu geben

  1. Auf jeden Fall eine unveränderliche Operation
  2. Für die Mehrheit der Benutzer verständlich
  3. Darstellbar in weniger als 4 Wörtern

Noch seltsamer wird es, wenn Sie andere Sammlungen als List betrachten. Nehmen Sie zum Beispiel Stack. Selbst Programmierer im ersten Jahr können Ihnen sagen, dass Stacks ein Push / Pop-Methodenpaar haben. Wenn Sie einen ImmutableStack erstellen und ihm einen völlig anderen Namen geben, nennen wir ihn Foo / Fop. Sie haben gerade mehr Arbeit hinzugefügt, damit sie Ihre Sammlung verwenden können.

Bearbeiten: Antwort auf Plus Edit

Ich sehe, wohin du mit Plus gehst. Ich denke, ein stärkerer Fall wäre tatsächlich Minus zum Entfernen. Wenn ich Folgendes sehen würde, würde ich mich sicherlich fragen, was in aller Welt der Programmierer dachte

list.Minus(obj);

Das größte Problem, das ich mit Plus / Minus oder einer neuen Paarung habe, ist, dass es sich wie ein Overkill anfühlt. Die Sammlung selbst hat bereits einen unterscheidenden Namen, das Präfix Unveränderlich. Warum noch weiter gehen, indem Sie Vokabeln hinzufügen, deren Absicht darin besteht, die gleiche Unterscheidung wie das bereits verwendete unveränderliche Präfix hinzuzufügen?

Ich kann das Argument der Aufrufseite sehen. Es macht es vom Standpunkt eines einzelnen Ausdrucks klarer. Im Kontext der gesamten Funktion erscheint dies jedoch unnötig.

Bearbeiten 2

Stimmen Sie zu, dass die Leute definitiv von String.Concat und DateTime.Add verwirrt wurden. Ich habe mehrere sehr kluge Programmierer gesehen, die auf dieses Problem gestoßen sind.

Ich denke jedoch, dass ImmutableList ein anderes Argument ist. Es gibt nichts an String oder DateTime, was es als unveränderlich für einen Programmierer festlegt. Sie müssen einfach wissen, dass es über eine andere Quelle unveränderlich ist. Die Verwirrung ist also nicht unerwartet.

ImmutableList hat dieses Problem nicht, da der Name sein Verhalten definiert. Man könnte argumentieren, dass die Leute nicht wissen, was unveränderlich ist, und ich denke, das gilt auch. Ich wusste es sicherlich erst im zweiten Jahr am College. Sie haben jedoch das gleiche Problem mit dem Namen, den Sie anstelle von Hinzufügen wählen.

Edit 3: Was ist mit Typen wie TestSuite, die unveränderlich sind, aber das Wort nicht enthalten?

Ich denke, das bringt die Idee auf den Punkt, dass Sie keine neuen Methodennamen erfinden sollten. Das heißt, es besteht eindeutig das Bestreben, Typen unveränderlich zu machen, um Paralleloperationen zu ermöglichen. Wenn Sie sich darauf konzentrieren, den Namen von Methoden für Sammlungen zu ändern, besteht der nächste Schritt darin, die Methodennamen für jeden von Ihnen verwendeten Typ zu ändern, der unveränderlich ist.

Ich denke, es wäre eine wertvollere Anstrengung, sich stattdessen darauf zu konzentrieren, Typen als unveränderlich identifizierbar zu machen. Auf diese Weise können Sie das Problem lösen, ohne jedes mutierende Methodenmuster zu überdenken.

Wie können Sie TestSuite als unveränderlich identifizieren? In der heutigen Umgebung gibt es meiner Meinung nach einige Möglichkeiten

  1. Präfix mit unveränderlich: ImmutableTestSuite
  2. Fügen Sie ein Attribut hinzu, das den Grad der Unveränderlichkeit beschreibt. Dies ist sicherlich weniger auffindbar
  3. Sonst nicht viel.

Ich vermute / hoffe, dass Entwicklungswerkzeuge dieses Problem lösen werden, indem sie es einfach machen, unveränderliche Typen einfach anhand des Sehens zu identifizieren (andere Farben, stärkere Schriftarten usw.). Aber ich denke, das ist die Antwort, wenn man alle Methodennamen ändert.


Habe Gründe für meine Präferenz für Plus gegenüber Hinzufügen zur Frage hinzugefügt. Würde Kommentare zu dieser Argumentation begrüßen. Ich bin wirklich offen für die Idee, dass ich dumm bin.
Jon Skeet

@JaredPar: Was ist, wenn der Typ TestSuite heißt?
Jon Skeet

Würde gerne noch +1 für Edit 3 geben, kann das aber natürlich nicht. (In meinem Fall versuche ich nicht, Parallelität zu erreichen - ich glaube, dass Unveränderlichkeit zu Code führt, über den man leichter nachdenken kann.) Ich bin immer noch nicht ganz davon überzeugt, dass Add der richtige Weg ist, aber die Unterstützung für Antworten hier ist überzeugend.
Jon Skeet

Wenn Sie diese Frage bei der Arbeit ins Gespräch bringen könnten, wäre das übrigens auch großartig. Ich habe die Diskussion auf einer C # -Liste begonnen, sodass möglicherweise bereits einige Kollegen beteiligt sind. Könnte auch intern bei Google fragen ...
Jon Skeet

@ Jon, ich glaube, ich kenne einen guten Ort, um diese Frage bei der Arbeit fallen zu lassen :)
JaredPar

27

Ich denke, dies kann eine dieser seltenen Situationen sein, in denen es akzeptabel ist, den +Bediener zu überlasten . In der mathematischen Terminologie wissen wir, dass +etwas nicht an das Ende von etwas anderem angehängt wird. Es kombiniert immer zwei Werte miteinander und gibt einen neuen resultierenden Wert zurück.

Zum Beispiel ist es intuitiv offensichtlich, wenn Sie sagen

x = 2 + 2;

der resultierende Wert von x ist 4, nicht 22.

Ähnlich,

var empty = new ImmutableList<string>();
var list1 = empty + "Hello";
var list2 = list1 + "immutable";
var list3 = list2 + "word";

sollte klarstellen, was jede Variable enthalten wird. Es sollte klar sein, dass dies list2nicht in der letzten Zeile geändert wird, sondern dass list3das Ergebnis des Anhängens von "Wort" zugewiesen wird list2.

Ansonsten würde ich nur die Funktion Plus () nennen.


Was ist, wenn Sie eine generische Liste haben, zu der Sie eine ganze Zahl (z. B.) oder eine andere Liste von ganzen Zahlen hinzufügen können ? Dann widerspricht diese Verwendung von + der Verkettung.
Finnw

@ Finnw: Ich verstehe deinen Standpunkt nicht. Bei einer Liste würde ich immer erwarten, dass + Anhängen bedeutet, unabhängig davon, ob Sie ein oder mehrere Elemente hinzufügen.
Bill the Lizard

4
Für eine wiederverwendbare API ist es eine gute Idee, auch eine benannte Methode zu haben, falls jemand Ihre Klasse aus einer Sprache ohne Überlastung des Operators verwendet.
Neil

"ob Sie ein oder mehrere Elemente hinzufügen" - was ist, wenn Sie die Liste als einzelnes Element hinzufügen möchten? dh [1] + 2 = [1,2]aber [1] + [2] = [1,[2]]. Was Sie vorschlagen, ist inkonsistentes Verhalten. Dies ist wahrscheinlich , warum Python nicht zulässt , dass Sie hinzufügen ein Element in dieser Art und Weise.
Mpen

Nun, in einer statisch typisierten Sprache wie C # können Sie den Typ der Listenelemente angeben, ob es sich um eine Liste von Listen oder eine Liste von Elementen handelt. Sie können also anhand des Typs entscheiden, ob ein Element hinzugefügt oder verkettet werden soll die beiden Listen.
Dobes Vandermeer

22

Um so klar wie möglich zu sein, möchten Sie vielleicht mit dem Wort CopyAndAddoder etwas Ähnlichem gehen.


Nicht für alle unveränderlichen Sammlungen ist jedoch eine Kopie erforderlich. Denken Sie an unveränderliche Bäume, sie brauchen nur einen neuen Knoten und können den vorhandenen Baum als Blatt verwenden.
JaredPar

Übrigens, ich habe Ihr Profil gelesen, bin dann in der richtigen Reihenfolge und war kurz erstaunt, wie alt Sie waren. Kurz darauf trat die Vernunft ein.
JaredPar

Zum ersten Kommentar: Ich benutze Bäume so selten, dass mir dieser Gedanke nie in den Sinn gekommen ist. Das ist ein guter Punkt.
Michael Myers

Zum zweiten Kommentar: Ich gestehe, ich habe es für das Abzeichen getan! Ich mag es einfach nicht, mein Alter preiszugeben. (Ich bin eigentlich jünger als du, aber ich kann den altmodischen Oldtimer ziemlich gut spielen, wenn ich muss. Und ich bin nicht der einzige, dessen Alter mit 89 angegeben ist.)
Michael Myers

Das Argument der unveränderlichen Bäume ist stark, ein guter Punkt. Dann würde EquivalentReferenceWithAdded (x) dieses Argument bekämpfen, klingt aber dumm und schwer abzuleiten.
TheBlastOne

21

Ich würde es Extend () oder ExtendWith () nennen, wenn Sie sich wirklich wortreich fühlen.

Erweitern bedeutet, etwas zu etwas anderem hinzuzufügen, ohne es zu ändern. Ich denke, dies ist eine sehr relevante Terminologie in C #, da dies dem Konzept der Erweiterungsmethoden ähnelt - sie "fügen" einer Klasse eine neue Methode hinzu, ohne die Klasse selbst zu "berühren".

Andernfalls, wenn Sie wirklich betonen möchten, dass Sie das ursprüngliche Objekt überhaupt nicht ändern, scheint die Verwendung eines Präfixes wie Get- für mich unvermeidlich.


1
Immer noch bedeutet es, etwas mit dem Basisobjekt zu tun.
Chaos

18

Added (), Appended ()

Ich verwende die Vergangenheitsform gerne für Operationen an unveränderlichen Objekten. Es vermittelt die Idee, dass Sie das ursprüngliche Objekt nicht ändern, und es ist leicht zu erkennen, wenn Sie es sehen.

Da mutierende Methodennamen häufig Präsensverben sind, gilt dies auch für die meisten Fälle, in denen Sie auf unveränderliche Methodennamen angewiesen sind. Zum Beispiel hat ein unveränderlicher Stapel die Methoden "Push" und "Popped".


1
Python verwendet diese Konvention ebenfalls. ZB eingebaute Funktionen umgekehrt () und sortiert ().
Joe

18

Ich mag mmyers Vorschlag von CopyAndAdd . In Übereinstimmung mit einem "Mutation" -Thema könnten Sie sich vielleicht für Bud (asexuelle Fortpflanzung), Grow , Replicate oder Evolve entscheiden ? =)

EDIT: Um mit meinem genetischen Thema fortzufahren, wie wäre es mit Procreate , was bedeutet, dass ein neues Objekt erstellt wird, das auf dem vorherigen basiert, aber mit etwas Neuem hinzugefügt wurde.


14

Dies ist wahrscheinlich eine Strecke, aber in Ruby gibt es eine häufig verwendete Notation für die Unterscheidung: addmutiert nicht; add!mutiert. Wenn dies ein weit verbreitetes Problem in Ihrem Projekt ist, können Sie dies auch tun (nicht unbedingt mit nicht alphabetischen Zeichen, sondern konsequent mit einer Notation, um mutierende / nicht mutierende Methoden anzugeben).


+1. Dies wird nicht durch die Sprache durchgesetzt, sondern als Konvention vereinbart. Ich mag es.
oma


13

Vielleicht liegt die Verwirrung daran, dass Sie zwei Operationen in einer wollen. Warum nicht trennen? DSL-Stil:

var list = new ImmutableList<string>("Hello");
var list2 = list.Copy().With("World!");

Copywürde ein Zwischenobjekt zurückgeben, das ist eine veränderbare Kopie der ursprünglichen Liste. Withwürde eine neue unveränderliche Liste zurückgeben.

Aktualisieren:

Es ist jedoch kein guter Ansatz, eine veränderliche Zwischensammlung zu haben. Das Zwischenobjekt sollte in der CopyOperation enthalten sein:

var list1 = new ImmutableList<string>("Hello");
var list2 = list1.Copy(list => list.Add("World!"));

Jetzt nimmt die CopyOperation einen Delegaten auf, der eine veränderbare Liste erhält, damit er das Kopierergebnis steuern kann. Es kann viel mehr als nur ein Element anhängen, z. B. Elemente entfernen oder die Liste sortieren. Es kann auch im ImmutableListKonstruktor verwendet werden, um die Anfangsliste ohne unveränderliche Zwischenlisten zusammenzustellen.

public ImmutableList<T> Copy(Action<IList<T>> mutate) {
  if (mutate == null) return this;
  var list = new List<T>(this);
  mutate(list);
  return new ImmutableList<T>(list);
}

Jetzt gibt es keine Möglichkeit einer Fehlinterpretation durch die Benutzer, sie werden natürlich in die Grube des Erfolgs fallen .

Noch ein Update:

Wenn Ihnen die Erwähnung der veränderlichen Liste auch jetzt noch nicht gefällt, können Sie ein Spezifikationsobjekt entwerfen, das angibt , wie der Kopiervorgang seine Liste transformiert , oder ein Skript . Die Verwendung ist die gleiche:

var list1 = new ImmutableList<string>("Hello");
// rules is a specification object, that takes commands to run in the copied collection
var list2 = list1.Copy(rules => rules.Append("World!"));

Jetzt können Sie mit den Regelnamen kreativ sein und nur die Funktionen verfügbar machen, die Sie unterstützen möchten Copy, nicht die gesamten Funktionen eines IList.

Für die Verwendung der Verkettung können Sie einen vernünftigen Konstruktor erstellen (der natürlich keine Verkettung verwendet):

public ImmutableList(params T[] elements) ...

...

var list = new ImmutableList<string>("Hello", "immutable", "World");

Oder verwenden Sie denselben Delegaten in einem anderen Konstruktor:

var list = new ImmutableList<string>(rules => 
  rules
    .Append("Hello")
    .Append("immutable")
    .Append("World")
);

Dies setzt voraus, dass die rules.AppendMethode zurückgibt this.

So würde es mit Ihrem neuesten Beispiel aussehen:

var suite = new TestSuite<string, int>(x => x.Length);
var otherSuite = suite.Copy(rules => 
  rules
    .Append(x => Int32.Parse(x))
    .Append(x => x.GetHashCode())
);

@Jordao: Weil diese Art der Komposition zu einer viel einfacheren Initialisierungsform führt. Warum zwei separate Variablen, wenn ich nur eine möchte? Ebenso möchte ich keine veränderbare Kopie erstellen - ich möchte, dass alles durchgehend unveränderlich ist, da dies zu Code führt, über den man leichter nachdenken kann, IMO.
Jon Skeet

All dies ist immer noch etwas unhandlicher als "Plus" usw. Vielen Dank für die Ideen zur Mutation, aber ich bevorzuge definitiv den Ansatz, es unveränderlich zu halten.
Jon Skeet

Vielleicht gibt es dann immer Fehlinterpretationen der tatsächlichen Semantik der Operation. Wenn Sie der Route des Spezifikationsobjekts folgen, findet keine Mutation statt (zumindest nicht extern). Das Objekt gibt nur an, wie das neue unveränderliche Objekt aussehen wird. Was Sie haben, ist eine Operation, die von einem unveränderlichen Objekt zum anderen geht, und da sie Kopieren heißt, gibt es keinen Raum für Missverständnisse.
Jordão

Die einzigen Namen, die für Sie funktionieren würden, scheinen zusammengesetzte Namen zu sein, wie CopyAndAppend, CopyAndAdd usw.
Jordão

1
Ich mag diesen Ansatz (mit allen Änderungen) als API für unveränderliche Sammlungen immer mehr. Allerdings würde ich argumentieren , dass dieser Ansatz mit Kombination Helfer Methoden für Standardspezifikationen, wie Concat bietet das Beste aus beiden Welten.
Hanf

11

Ein paar zufällige Gedanken:

  • ImmutableAdd ()
  • Anhängen ()
  • Konstruktor ImmutableList <T> (ImmutableList <T> originalList, T newItem)

1
+1 für ImmutableAdd; Ich bin nicht scharf auf Append (zu sehr wie StringBuilder.Append, das mutiert) und die Konstruktorversion ist ein Problem in Bezug auf die Verkettung.
Jon Skeet

10

DateTime in C # verwendet Add. Warum also nicht den gleichen Namen verwenden? Solange die Benutzer Ihrer Klasse verstehen, dass die Klasse unveränderlich ist.


Ich würde argumentieren, dass DateTime.Add bekanntermaßen Menschen verwirrt ... aber ich stimme zu, dass es Vorrang hat.
Jon Skeet

1
Genau wie Methoden zum Mutieren eines Strings für neue Entwickler verwirrend sind. Aber schon bald erfährt jeder, dass "Saiten unveränderlich sind"
Tundey

9

Ich denke, der Schlüssel, den Sie erreichen wollen, der schwer auszudrücken ist, ist die Nichtpermutation, also vielleicht etwas mit einem generativen Wort, so etwas wie CopyWith () oder InstancePlus ().


9

Ich glaube nicht, dass die englische Sprache es Ihnen ermöglicht, Unveränderlichkeit auf unverkennbare Weise zu implizieren, während Sie ein Verb verwenden, das dasselbe bedeutet wie "Hinzufügen". "Plus" macht es fast, aber die Leute können immer noch den Fehler machen.

Die einzige Möglichkeit, um zu verhindern, dass Ihre Benutzer das Objekt mit etwas Veränderlichem verwechseln, besteht darin, es explizit zu machen, entweder durch den Namen des Objekts selbst oder durch den Namen der Methode (wie bei den ausführlichen Optionen wie "GetCopyWith" oder " "CopyAndAdd").

Also geh einfach mit deinem Favoriten "Plus".


9

Zunächst ein interessanter Ausgangspunkt: http://en.wikipedia.org/wiki/Naming_conventions_(programming) ... Überprüfen Sie insbesondere die Links "Siehe auch" unten.

Ich bin für Plus oder Und, effektiv gleichermaßen.

Plus und And basieren beide auf Mathematik in der Etymologie. Als solche bedeuten beide mathematische Operationen; beide ergeben einen Ausdruck, der natürlich als Ausdrücke gelesen wird, die sich in einen Wert auflösen können, der zu der Methode mit einem Rückgabewert passt. Andträgt zusätzliche logische Konnotation, aber beide Wörter gelten intuitiv für Listen. Addbezeichnet eine an einem Objekt ausgeführte Aktion, die im Widerspruch zur unveränderlichen Semantik der Methode steht.

Beide sind kurz, was angesichts der Primitivität der Operation besonders wichtig ist. Einfache, häufig ausgeführte Operationen verdienen kürzere Namen.

Unveränderliche Semantik auszudrücken, ist etwas, das ich lieber über den Kontext mache. Das heißt, ich würde eher einfach implizieren, dass dieser gesamte Codeblock ein funktionales Gefühl hat; Angenommen, alles ist unveränderlich. Das könnte aber nur ich sein. Ich bevorzuge Unveränderlichkeit als Regel; Wenn es fertig ist, wird es viel am selben Ort gemacht. Veränderlichkeit ist die Ausnahme.


8

Wie wäre es mit Chain () oder Attach ()?


1
Anhängen klingt sicherlich nach Mutation. Vielleicht wäre WithAttached klüger, steht aber WithAdded sehr nahe, was oben besprochen wurde.
TheBlastOne

Es ist lustig, wie oft "Verketten" erwähnt wird, um zu beschreiben, was damit versucht werden soll. (5 mal), aber nicht als Vorschlag angeboten! Es ist das erste, was visualthesaurus.combei meiner Suche auftauchte (keine Zugehörigkeit) concatenate.
cod3monk3y

7

Ich bevorzuge Plus (und Minus). Sie sind leicht verständlich und lassen sich direkt auf Operationen mit bekannten unveränderlichen Typen (den Zahlen) abbilden. 2 + 2 ändert den Wert von 2 nicht, sondern gibt einen neuen, ebenso unveränderlichen Wert zurück.

Einige andere Möglichkeiten:

Spleißen()

Transplantat()

Accrete ()


6

Wie wäre es mit Kumpel , Kumpel mit oder Koitus für diejenigen, die bleiben. In Bezug auf die Fortpflanzung gelten Säugetiere im Allgemeinen als unveränderlich.

Ich werde Union auch da rauswerfen. Aus SQL entlehnt.


2
+1 für Union. Ich leihe es mir jedoch direkt aus der Mengenlehre aus. :-)
Christoffer Lette

Bekommt SQL nicht viel von seiner Terminologie aus der Mengenlehre?
Jason D

1
Außerdem impliziert Union eine Unterscheidung. Wenn Sie Duplikate einschließen möchten (zumindest in TSQL), müssen Sie Union All explizit verwenden.
Hanf

6

Anscheinend bin ich die erste Obj-C / Cocoa-Person, die diese Frage beantwortet.

NNString *empty = [[NSString alloc] init];
NSString *list1 = [empty stringByAppendingString:@"Hello"];
NSString *list2 = [list1 stringByAppendingString:@"immutable"];
NSString *list3 = [list2 stringByAppendingString:@"word"];

Ich werde damit keine Code-Golfspiele gewinnen.


+1 Das [object]By[Verb]ing[Object]:Präfix der Methode impliziert, dass im Gegensatz zu einfach eine neue Zeichenfolge zurückgegeben wird [verb][Object]:, die das Objekt an Ort und Stelle mutieren würde.
Dave DeLong

3
+1. Ich verstehe die Abneigung der Menschen gegen Ausführlichkeit (auch bekannt als Selbstdokumentation) nicht.
Hatfinch

5

Ich denke, "Hinzufügen" oder "Plus" klingt gut. Der Name der Liste selbst sollte ausreichen, um die Unveränderlichkeit der Liste zu vermitteln.


Es ist keine Liste, es ist eine Testsuite.
Tstenner

5

Vielleicht gibt es einige Wörter, die mich eher daran erinnern, eine Kopie zu erstellen und Dinge hinzuzufügen, anstatt die Instanz zu mutieren (wie "Verketten"). Aber ich denke, eine gewisse Symmetrie für diese Wörter für andere Handlungen wäre auch gut zu haben. Ich kenne kein ähnliches Wort für "Entfernen", das ich für die gleiche Art wie "Verketten" halte. "Plus" klingt für mich etwas seltsam. Ich würde nicht erwarten, dass es in einem nicht numerischen Kontext verwendet wird. Aber das könnte auch von meinem nicht-englischen Hintergrund kommen.

Vielleicht würde ich dieses Schema verwenden

AddToCopy
RemoveFromCopy
InsertIntoCopy

Diese haben jedoch ihre eigenen Probleme, wenn ich darüber nachdenke. Man könnte denken, sie entfernen etwas oder fügen etwas zu einem gegebenen Argument hinzu. Ich bin mir überhaupt nicht sicher. Diese Worte spielen auch beim Verketten nicht gut, denke ich. Zu wortreich zum tippen.

Vielleicht würde ich einfach nur "Hinzufügen" und Freunde verwenden. Mir gefällt, wie es in der Mathematik verwendet wird

Add 1 to 2 and you get 3

Nun, natürlich bleibt eine 2 eine 2 und Sie erhalten eine neue Zahl. Hier geht es um zwei Zahlen und nicht um eine Liste und ein Element, aber ich denke, es hat eine Analogie. Meiner Meinung nach addbedeutet das nicht unbedingt, dass Sie etwas mutieren. Ich verstehe sicherlich Ihren Standpunkt, dass eine einsame Aussage, die nur ein enthält addund das zurückgegebene neue Objekt nicht verwendet, nicht fehlerhaft aussieht. Aber ich habe jetzt auch einige Zeit über die Idee nachgedacht, einen anderen Namen als "hinzufügen" zu verwenden, aber ich kann mir einfach keinen anderen Namen einfallen lassen, ohne mich zum Nachdenken zu bringen "hmm, ich müsste mir die Dokumentation ansehen, um zu wissen, was es geht um ", weil sein Name sich von dem unterscheidet, was ich als" hinzufügen "erwarten würde. Nur ein paar seltsame Gedanken von litb darüber, nicht sicher, ob es überhaupt Sinn macht :)


In einer unveränderlichen Sammlung ist jedoch nicht immer eine Kopie erforderlich. Nehmen Sie zum Beispiel einen Binärbaum. Das Hinzufügen einer neuen Wurzel erfordert kein Kopieren, sondern nur einen neuen Wert, bei dem eines der Blätter der alte Baum ist
JaredPar

KISS - Wenn die Benennung Implementierungsdetails enthalten würde, würden alle Methodennamen zu lang werden. "add" ist einfach genug und erledigt den Job.
mP.

1
@mP: Wieder verwenden Sie den Ausdruck "Implementierungsdetail" auf eine Weise, die ich für ungeeignet halte. Ob es sich um eine verknüpfte Liste oder ein Array unter der Haube handelt, ist ein Implementierungsdetail . Ich könnte die Implementierung ändern , ohne die API zu ändern. Der unveränderliche Aspekt ist kein Implementierungsdetail.
Jon Skeet

1
richtig. JaredPar, aber ich denke, es ist auch nicht wichtig, ob es den Baum tatsächlich kopiert oder nur den vorhandenen Baum verwendet und ihn als Blatt für den zurückgegebenen neuen Baum verwendet. Ich meine, das ist nur ein Implementierungsdetail. Es ist dasselbe für (ich glaube) die Teilstring-Operation für die String-Klasse von Java.
Johannes Schaub - litb


5

Ich denke , dass Plus()und Minus()oder alternativ Including(), Excluding()ist angemessen zu implizieren unveränderliche Verhalten.

Allerdings wird keine Namenswahl jemals jedem klar machen, daher glaube ich persönlich, dass ein guter XML-Dokumentkommentar hier einen sehr langen Weg gehen würde. VS wirft Ihnen diese direkt ins Gesicht, wenn Sie Code in die IDE schreiben - sie sind schwer zu ignorieren.


4

Append- Beachten Sie, dass die Namen der System.StringMethoden darauf hindeuten, dass sie die Instanz mutieren, dies jedoch nicht.

Oder ich mag ganz AfterAppending:

void test()
{
  Bar bar = new Bar();
  List list = bar.AfterAppending("foo");
}

Wie das DateTime-Argument zuvor würde ich der Priorität zustimmen, wenn nicht so viele Programmierer erwarten würden , dass String-Methoden etwas tun. Ihnen muss (manchmal wiederholt) gesagt werden, dass Zeichenfolgen unveränderlich sind. Ich möchte meine Kundenentwickler in die Grube des Erfolgs locken :)
Jon Skeet

Ich mag Append immer mehr. Es unterscheidet, dass Sie die Sammlung nicht mutieren, sondern sich ihr anschließen.

4

list.CopyWith(element)

Wie auch Smalltalk :)

Außerdem werden dadurch list.copyWithout(element)alle Vorkommen eines Elements list.copyWithout(null)entfernt. Dies ist besonders nützlich, wenn nicht gesetzte Elemente entfernt werden sollen.


3

Ich würde mich für Hinzufügen entscheiden, weil ich den Vorteil eines besseren Namens sehen kann, aber das Problem wäre, für jede andere unveränderliche Operation unterschiedliche Namen zu finden, was die Klasse möglicherweise ziemlich ungewohnt macht, wenn dies sinnvoll ist.


Ja, ich verstehe deinen Standpunkt. Ich denke, ich bevorzuge immer noch Plus, aber es ist ein erwägenswerter Punkt.
Jon Skeet

Danke Jon. Ich freue mich darauf, Ihr Framework zu verwenden. Ich habe gerade darüber gelesen und es sieht sehr gut aus. Danke noch einmal.
Joan Venge
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.