Warum ist das Minuszeichen '-' im Allgemeinen nicht so überladen wie das Pluszeichen?

64

Das Pluszeichen +wird zur Addition und zur Verkettung von Zeichenfolgen verwendet, aber sein Begleiter: Das Minuszeichen -wird im Allgemeinen nicht zum Trimmen von Zeichenfolgen oder in einem anderen Fall als der Subtraktion verwendet. Was könnte der Grund oder die Einschränkungen dafür sein?

Betrachten Sie das folgende Beispiel in JavaScript:

var a = "abcdefg";
var b = "efg";

a-b == NaN
// but
a+b == "abcdefgefg"
Digvijay Yadav
quelle
35
welches "jj" soll entfernt werden?
Gaschach
12
Wenn ich mit dem Verhalten des Pluszeichens gehe, dann macht das Richtigste Sinn zu.
Digvijay Yadav
46
Es ist schon schlimm genug, dass der Binäroperator +mit den beiden völlig unabhängigen Bedeutungen „numerische Addition“ und „String-Verkettung“ überladen ist. Zum Glück bieten einige Sprachen einen separaten Verkettungsoperator wie .(Perl5, PHP), ~(Perl6), &(VB), ++(Haskell), ...
amon
6
@ MasonWheeler Sie verwenden ->(denken Sie, dass der Zugriff von Mitgliedern in C dereferenziert wird, da virtuelle Methodenaufrufe notwendigerweise eine zeigerartige Indirektion beinhalten). Es gibt kein Gesetz des Sprachdesigns, das Methodenaufrufe / Mitgliederzugriff erfordert, um einen .Operator zu verwenden, obwohl dies eine zunehmend verbreitete Konvention ist. Wussten Sie, dass Smalltalk keinen Methodenaufrufoperator hat? Ein einfaches Nebeneinander object methodreicht aus.
amon
20
Python tut Überlastung minus, für Satz Subtraktion (und es kann auch in benutzerdefinierten Typen überlastet). Python-Sets überladen auch die meisten bitweisen Operatoren für intersection / union / etc.
Kevin

Antworten:

116

Kurz gesagt, es gibt keine besonders nützlichen subtraktionsähnlichen Operationen für Zeichenfolgen, mit denen die Leute Algorithmen schreiben wollten.

Der +Operator bezeichnet im Allgemeinen die Operation eines additiven Monoids , dh eine assoziative Operation mit einem Identitätselement:

  • A + (B + C) = (A + B) + C
  • A + 0 = 0 + A = A

Es ist sinnvoll, diesen Operator für Dinge wie Ganzzahladdition, Zeichenfolgenverkettung und Mengenvereinigung zu verwenden, da alle dieselbe algebraische Struktur haben:

1 + (2 + 3) == (1 + 2) + 3
1 + 0 == 0 + 1 == 1

"a" + ("b" + "c") == ("a" + "b") + "c"
"a" + "" == "" + "a" == "a"

Und wir können damit handliche Algorithmen schreiben, wie eine concatFunktion, die mit einer Folge von „verkettbaren“ Dingen arbeitet, z.

def concat(sequence):
    return sequence.reduce(+, 0)

Wenn Subtraktion -involviert ist, spricht man normalerweise über die Struktur einer Gruppe , die für jedes Element A eine Inverse - A hinzufügt , so dass:

  • A + - A = - A + A = 0

Und während dies für Dinge wie Ganzzahl- und Gleitkommasubtraktion Sinn macht oder sogar Unterschiede festlegt, macht es für Strings und Listen nicht so viel Sinn. Was ist das Gegenteil von "foo"?

Es gibt eine Struktur, die als abbrechendes Monoid bezeichnet wird und keine Inversen, sondern die Eigenschaft cancel hat , sodass:

  • A - A = 0
  • A - 0 = A
  • (A + B) - B = A

Dies ist die Struktur, die Sie beschreiben, wo "ab" - "b" == "a", aber "ab" - "c"nicht definiert ist. Es ist nur so, dass wir nicht viele nützliche Algorithmen haben, die diese Struktur verwenden. Ich denke, wenn Sie Verkettung als Serialisierung betrachten, könnte Subtraktion für eine Art Analyse verwendet werden.

Jon Purdy
quelle
2
Für Mengen (und Mehrmengen) ist die Subtraktion sinnvoll, da im Gegensatz zu Sequenzen die Reihenfolge des Elements keine Rolle spielt.
CodesInChaos
@CodesInChaos: Ich habe sie erwähnt, aber ich habe es nicht wirklich gemocht, Sätze als Beispiel für eine Gruppe zu verwenden - ich glaube nicht, dass sie einen bilden, da man die Umkehrung eines Satzes im Allgemeinen nicht konstruieren kann.
Jon Purdy
12
Tatsächlich ist die +Operation auch kommutativ für Zahlen, A+B == B+Awas sie zu einem schlechten Kandidaten für die Verkettung von Zeichenfolgen macht. Dies und die verwirrende Rangfolge der Operatoren machen die Verwendung +von Zeichenfolgen für die Verkettung zu einem historischen Fehler. Es ist jedoch wahr, dass die Verwendung -einer beliebigen String-Operation die Sache noch schlimmer gemacht hat…
Holger
2
@ Darkhogg: Richtig! PHP .von Perl ausgeliehen; Es ist ~in Perl6, möglicherweise auch in anderen.
Jon Purdy
1
@MartinBeckett, aber Sie können sehen, dass das Verhalten verwirrend sein könnte mit .text.gz.text...
Boris the Spider
38

Da die Verkettung von zwei gültigen Zeichenfolgen immer eine gültige Operation ist, ist das Gegenteil nicht der Fall.

var a = "Hello";
var b = "World";

Was soll a - bhier sein? Es gibt wirklich keine gute Möglichkeit, diese Frage zu beantworten, da die Frage selbst nicht gültig ist.

Mason Wheeler
quelle
31
@ DigvijayYadav, wenn Sie 5 Mangos von 5 Äpfeln entfernen, muss es dann einen Zähler von -5 Mangos geben? Tut es nichts Können Sie dies so gut definieren, dass es allgemein akzeptiert und in alle Compiler und Interpreten von Sprachen übernommen werden kann, um diesen Operator in dieser Form zu verwenden? Das ist hier die große Herausforderung.
JB King
28
@DigvijayYadav: Sie haben also gerade zwei Möglichkeiten beschrieben, dies zu implementieren, und es gibt ein gutes Argument, um jede als gültig zu betrachten. Wir machen also bereits ein Chaos mit der Idee, diese Operation zu spezifizieren. : P
Mason Wheeler
13
@smci Scheint mir 5 + Falsenatürlich ein Fehler zu sein , da eine Zahl kein Boolescher Wert und ein Boolescher Wert keine Zahl ist.
Mason Wheeler
6
@ JanDvorak: Daran ist nichts besonders "Haskelly"; Das ist eine grundlegende, starke Schreibweise.
Mason Wheeler
5
@ DigvijayYadav Also (a+b)-b = a(hoffentlich!), Aber (a-b)+bist das manchmal a, manchmal a+babhängig davon, ob bein Teilstring vorliegt aoder nicht? Was für ein Wahnsinn ist das?
28

Weil der -Operator für die String-Manipulation nicht genügend "semantischen Zusammenhalt" hat. Operatoren sollten nur dann überladen werden, wenn absolut klar ist, was die Überladung mit ihren Operanden bewirkt, und die Zeichenfolgensubtraktion diesen Balken nicht erfüllt.

Folglich werden Methodenaufrufe bevorzugt:

public string Remove(string source, string toRemove)
public string Replace(string source, string oldValue, string newValue)

In der C # -Sprache verwenden wir +für die Verkettung der Zeichenfolgen das Formular

var result = string1 + string2 + string3;

Anstatt von

var result = string.Concat(string1, string2, string3);

ist praktisch und wahrscheinlich leichter zu lesen, obwohl ein Funktionsaufruf vom semantischen Standpunkt aus wahrscheinlich "korrekter" ist.

Der +Bediener kann in diesem Zusammenhang wirklich nur eines bedeuten. Das ist nicht so wahr für -, da der Begriff der Subtraktion Strings ist nicht eindeutig (der Funktionsaufruf Replace(source, oldValue, newValue)mit ""als der newValueParameter entfernt alle Zweifel, und die Funktion kann verwendet werden , Strings zu verändern, nicht nur um sie zu entfernen).

Das Problem ist natürlich, dass die Überladung des Operators von den Typen abhängt, die an den Operator übergeben werden. Wenn Sie eine Zeichenfolge übergeben, bei der eine Zahl hätte stehen müssen, erhalten Sie möglicherweise ein Ergebnis, das Sie nicht erwartet haben. Darüber hinaus ist für viele Verkettungen (dh in einer Schleife) ein StringBuilderObjekt vorzuziehen, da bei jeder Verwendung von +eine brandneue Zeichenfolge erstellt wird und die Leistung darunter leiden kann. Der +Bediener ist also nicht in allen Zusammenhängen angemessen.

Es gibt Operatorüberladungen, die eine bessere semantische Kohäsivität aufweisen als der +Operator für die Verkettung von Zeichenfolgen. Hier ist eine, die zwei komplexe Zahlen hinzufügt:

public static Complex operator +(Complex c1, Complex c2) 
{
    return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}
Robert Harvey
quelle
8
+1 Bei zwei gegebenen Zeichenfolgen, A und B, kann ich mir AB vorstellen als "ein abschließendes B vom Ende von A entfernen", "eine Instanz von B von irgendwo in A entfernen", "alle Instanzen von B von irgendwo in A entfernen , "oder sogar" entferne alle in B gefundenen Zeichen von A. "
Cort Ammon
8

Die Groovy-Sprache erlaubt -:

println('ABC'-'B')

kehrt zurück:

AC

Und:

println( 'Hello' - 'World' )

kehrt zurück:

Hello

Und:

println('ABABABABAB' - 'B')

kehrt zurück:

AABABABAB
Wim Deblauwe
quelle
11
Interessant - so wählt es das erste Vorkommen zu entfernen? Ein gutes Beispiel für ein völlig kontraintuitives Verhalten.
Hulk
9
Wir haben also nicht ('ABABABABA' + 'B') - 'B'annähernd den gleichen Wert wie der Startwert 'ABABABABA'.
ein Lebenslauf vom
3
@ MichaelKjörling OTOH, (A + B) - A == Bfür jedes A und B. Kann ich das eine Links-Subtraktion nennen?
John Dvorak
2
Haskell hat ++für die Verkettung. Es funktioniert auf jeder Liste und eine Zeichenfolge ist nur eine Liste von Zeichen. Es hat auch \\, was das erste Vorkommen jedes Elements im rechten Argument aus dem linken Argument entfernt.
John Dvorak
3
Ich denke, diese Beispiele sind genau der Grund, warum es keinen Minus-Operator für Zeichenfolgen geben sollte. Es ist inkonsistent und kein intuitives Verhalten. Wenn ich an "-" denke, denke ich nicht, "entferne die erste Instanz der übereinstimmenden Zeichenfolge, falls sie auftritt, andernfalls tue ich einfach nichts."
Enderland
6

Das Pluszeichen ist wahrscheinlich in mehreren Fällen kontextbezogen sinnvoll, aber ein Gegenbeispiel (möglicherweise eine Ausnahme, die die Regel bestätigt) in Python ist das Set-Objekt, das Folgendes vorsieht, -aber nicht +:

>>> set('abc') - set('bcd')
set(['a'])
>>> set('abc') + set('bcd')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'set' and 'set'

Es macht keinen Sinn, das +Zeichen zu verwenden , weil die Absicht mehrdeutig sein könnte - bedeutet es, Schnittmenge oder Vereinigung zu setzen? Stattdessen wird |für Vereinigung und &für Schnitt verwendet:

>>> set('abc') | set('bcd')
set(['a', 'c', 'b', 'd'])
>>> set('abc') & set('bcd')
set(['c', 'b'])
Aaron Hall
quelle
2
Dies ist wahrscheinlicher, weil die Mengen-Subtraktion in Mathe definiert ist, die Mengen-Addition jedoch nicht.
Mehrdad
Die Verwendung von "-" scheint zweifelhaft; Was wirklich benötigt wird, ist ein "aber nicht" -Operator, der auch nützlich wäre, wenn bitweise Arithmetik mit ganzen Zahlen durchgeführt wird. Wenn 30 ~ & 7 24 wären, würde die Verwendung von ~ & mit Mengen gut zu & und | passen Auch wenn Sets keinen ~ -Operator haben.
Supercat
1
set('abc') ^ set('bcd')kehrt zurück set(['a', 'd']), wenn Sie nach dem symmetrischen Unterschied fragen.
Aaron Hall
3

" -" wird in einigen zusammengesetzten Wörtern verwendet (z. B. "vor Ort"), um die verschiedenen Teile zu einem Wort zusammenzufügen. Warum verwenden wir nicht " -", um verschiedene Zeichenfolgen in Programmiersprachen zusammenzufügen? Ich denke, es würde vollkommen Sinn machen! Zum Teufel mit diesem +Unsinn!

Betrachten wir dies jedoch aus einem etwas abstrakteren Blickwinkel.

Wie würden Sie String-Algebra definieren? Welche Operationen hätten Sie und welche Gesetze würden für sie gelten? Wie würden ihre Beziehungen sein?

Denken Sie daran, es darf absolut keine Mehrdeutigkeit geben! Jeder mögliche Fall muss genau definiert sein, auch wenn es bedeutet, dass dies nicht möglich ist! Je kleiner Ihre Algebra ist, desto einfacher ist dies.

Was bedeutet es beispielsweise, zwei Zeichenfolgen zu addieren oder zu subtrahieren?

Wenn Sie zwei Zeichenfolgen hinzufügen (zum Beispiel let a = "aa"und b = "bb"), erhalten Sie aabbals Ergebnis von a + b?

Wie wäre es b + a? Wäre das bbaa? Warum nicht aabb? Was passiert, wenn Sie aavom Ergebnis Ihrer Addition abziehen ? Hätte Ihre Saite ein Konzept von negativem Inhalt aa?

Kehren Sie nun zum Anfang dieser Antwort zurück und ersetzen Sie spaceshuttledie Zeichenfolge. Warum ist eine Operation für einen Typ definiert oder nicht definiert?

Der Punkt, den ich versuche, ist, dass nichts Sie daran hindert, eine Algebra für irgendetwas zu erstellen. Es kann schwierig sein, sinnvolle Operationen oder sogar nützliche Operationen dafür zu finden.

Für Streicher ist die Verkettung so ziemlich die einzig sinnvolle, die mir jemals begegnet ist. Es spielt keine Rolle, welches Symbol für den Vorgang verwendet wird.

Verhalten
quelle
1
"Für Streicher ist die Verkettung so ziemlich die einzig sinnvolle, die mir jemals begegnet ist" . Stimmen Sie dann nicht mit Pythons überein 'xy' * 3 == 'xyxyxy'?
smci
3
@smci, das ist doch nur Multiplikation-als-wiederholte-Addition ?
Jonrsharpe
Was ist der richtige Operator zum Verketten von Spaceshuttles?
Mr.Mindor
4
@ Mr.Mindor Rücktaste ..., um den Abstand zwischen den Spaceshuttles zu entfernen.
YoungJohn