Perché il segno meno, '-', generalmente non è sovraccaricato allo stesso modo del segno più?


64

Il segno più +viene usato per l'aggiunta e per la concatenazione di stringhe, ma il suo compagno: il segno meno -, generalmente non viene visto per il taglio di stringhe o altri casi diversi dalla sottrazione. Quale potrebbe essere la ragione o le limitazioni per questo?

Considera il seguente esempio in JavaScript:

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

a-b == NaN
// but
a+b == "abcdefgefg"

35
quale "yy" dovrebbe essere rimosso?
gashach,

12
Se vado con il comportamento del segno '+', allora il più giusto ha senso.
Digvijay Yadav,

46
È abbastanza grave che l' +operatore binario sia sovraccarico di due significati totalmente non correlati "aggiunta numerica" ​​e "concatenazione di stringhe". Per fortuna, alcune lingue forniscono un operatore di concatenazione separato come .(Perl5, PHP), ~(Perl6), &(VB), ++(Haskell), ...
Amon,

6
@MasonWheeler Usano ->(pensa a dereferenziare l'accesso dei membri in C, poiché le chiamate al metodo virtuale implicano necessariamente un riferimento indiretto simile a un puntatore). Non esiste una legge di progettazione del linguaggio che richieda chiamate di metodo / accesso dei membri per utilizzare un .operatore, sebbene sia una convenzione sempre più comune. Sapevi che Smalltalk non ha un operatore di chiamata del metodo? La giustapposizione semplice object methodè sufficiente.
amon,

20
Python fa sovraccarico di meno, per il set di sottrazione (e può essere sovraccaricato di tipi definiti dall'utente pure). I set di Python inoltre sovraccaricano la maggior parte degli operatori bit per intersezione / unione / ecc.
Kevin,

Risposte:


116

In breve, non ci sono operazioni simili a sottrazioni particolarmente utili su stringhe con cui le persone hanno voluto scrivere algoritmi.

L' +operatore generalmente indica l'operazione di un monoide additivo , cioè un'operazione associativa con un elemento di identità:

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

Ha senso usare questo operatore per cose come l'aggiunta di numeri interi, la concatenazione di stringhe e l'unione di set perché hanno tutti la stessa struttura algebrica:

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

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

E possiamo usarlo per scrivere pratici algoritmi come una concatfunzione che funziona su una sequenza di qualsiasi cosa "concatenabile", ad esempio:

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

Quando -viene coinvolta la sottrazione , di solito si parla della struttura di un gruppo , che aggiunge un -A inverso per ogni elemento A, in modo che:

  • A + −A = −A + A = 0

E mentre questo ha senso per cose come la sottrazione di numeri interi e in virgola mobile, o addirittura imposta la differenza, non ha molto senso per le stringhe e gli elenchi. Qual è l'inverso di "foo"?

Esiste una struttura chiamata monoid cancellativo , che non ha inversioni, ma ha la proprietà di annullamento , in modo che:

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

Questa è la struttura che descrivi, dove "ab" - "b" == "a", ma "ab" - "c"non è definita. È solo che non abbiamo molti algoritmi utili che utilizzano questa struttura. Immagino che se pensi alla concatenazione come serializzazione, la sottrazione potrebbe essere utilizzata per un qualche tipo di analisi.


2
Per la sottrazione di insiemi (e insiemi multipli) ha senso, perché a differenza delle sequenze, l'ordine dell'elemento non ha importanza.
CodesInChaos,

@CodesInChaos: ne ho aggiunto una menzione, ma non mi sentivo molto a mio agio nel mettere i set come esempio di un gruppo: non credo che formino uno, poiché generalmente non è possibile costruire l'inverso di un set.
Jon Purdy,

12
In realtà, l' +operazione è anche commutativa per i numeri, vale a dire A+B == B+A, il che la rende un cattivo candidato per la concatenazione di stringhe. Questo, oltre alla confusa precedenza dell'operatore, rende l'utilizzo +per la concatenazione di stringhe un errore storico. Tuttavia, è vero che l'utilizzo -per qualsiasi operazione di stringa ha peggiorato le cose ...
Holger,

2
@Darkhogg: giusto! PHP preso .in prestito da Perl; è ~in Perl6, forse altri.
Jon Purdy,

1
@MartinBeckett ma puoi vedere che il comportamento potrebbe essere fonte di confusione con .text.gz.text...
Boris the Spider

38

Perché la concatenazione di due stringhe valide è sempre un'operazione valida, ma non è vero il contrario.

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

Cosa dovrebbe a - bessere qui? Non c'è davvero un buon modo per rispondere a questa domanda, perché la domanda stessa non è valida.


31
@DigvijayYadav, se rimuovi 5 mango da 5 mele ci deve essere un contatore di -5 mango? Non fa niente? Puoi definirlo abbastanza bene da poter essere ampiamente accettato e inserito in tutti i compilatori e interpreti di lingue per utilizzare questo operatore in questo modulo? Questa è la grande sfida qui.
JB King,

28
@DigvijayYadav: Quindi hai appena descritto due possibili modi per implementarlo, e c'è un buon argomento per considerarli validi, quindi stiamo già facendo un casino dell'idea di specificare questa operazione. : P
Mason Wheeler,

13
@smci Mi sembra 5 + Falseovviamente un errore , dato che un numero non è un valore booleano e un valore booleano non è un numero.
Mason Wheeler,

6
@JanDvorak: Non c'è nulla di particolarmente "Haskelly" in questo; questa è una tipizzazione forte di base.
Mason Wheeler,

5
@DigvijayYadav Quindi (a+b)-b = a(si spera!), Ma a (a-b)+bvolte a, a volte a+bdipende se bè una sottostringa ao no? Che follia è questa?

28

Perché l' -operatore per la manipolazione delle stringhe non ha abbastanza "coesione semantica". Gli operatori dovrebbero essere sovraccaricati solo quando è assolutamente chiaro cosa fa il sovraccarico con i suoi operandi e la sottrazione di stringhe non soddisfa quella barra.

Di conseguenza, sono preferite le chiamate di metodo:

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

Nel linguaggio C #, usiamo +per la concatenazione di stringhe perché il modulo

var result = string1 + string2 + string3;

invece di

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

è conveniente e probabilmente più facile da leggere, anche se una chiamata di funzione è probabilmente più "corretta" da un punto di vista semantico.

L' +operatore può davvero significare solo una cosa in questo contesto. Questo non è altrettanto vero -, poiché la nozione di sottrazione di stringhe è ambigua (la chiamata di funzione Replace(source, oldValue, newValue)con ""come newValueparametro rimuove tutti i dubbi e la funzione può essere utilizzata per modificare le sottostringhe, non solo per rimuoverle).

Il problema, ovviamente, è che il sovraccarico dell'operatore dipende dai tipi che vengono passati all'operatore e se si passa una stringa in cui un numero avrebbe dovuto essere, è possibile che si ottenga un risultato non previsto. Inoltre, per molte concatenazioni (cioè in un ciclo), StringBuilderè preferibile un oggetto, poiché ogni utilizzo di +crea una stringa nuova di zecca e le prestazioni possono risentirne. Quindi l' +operatore non è nemmeno appropriato in tutti i contesti.

Esistono sovraccarichi dell'operatore che hanno una migliore coesione semantica rispetto +all'operatore per la concatenazione di stringhe. Eccone uno che aggiunge due numeri complessi:

public static Complex operator +(Complex c1, Complex c2) 
{
    return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}

8
+1 Dato due stringhe, A e B, posso pensare ad AB come "rimuovi una B finale dalla fine di A", "rimuovi un'istanza di B da qualche parte in A", "rimuovi tutte le istanze di B da qualche parte in A , "o addirittura" rimuovi tutti i caratteri trovati in B da A. "
Cort Ammon,

8

La lingua Groovy consente -:

println('ABC'-'B')

ritorna:

AC

E:

println( 'Hello' - 'World' )

ritorna:

Hello

E:

println('ABABABABAB' - 'B')

ritorna:

AABABABAB

11
Interessante - quindi sceglie di rimuovere la prima occorrenza? Un buon esempio per un comportamento completamente controintuitivo.
Hulk,

9
Quindi, non abbiamo ('ABABABABA' + 'B') - 'B'nulla simile al valore iniziale 'ABABABABA'.
un CVn

3
@ MichaelKjörling OTOH, (A + B) - A == Bper ogni A e B. Posso chiamare quella sottrazione sinistra?
John Dvorak,

2
Haskell ha ++per la concatenazione. Funziona su qualsiasi elenco e una stringa è solo un elenco di caratteri. Ha anche \\, che rimuove la prima occorrenza di ogni elemento nell'argomento destro dall'argomento sinistro.
John Dvorak,

3
Sento che questi esempi sono esattamente il motivo per cui non dovrebbe esserci un operatore meno per le stringhe. È un comportamento incoerente e non intuitivo. Quando penso a "-" Sicuramente non penso ", rimuovi la prima istanza della stringa corrispondente, se si verifica, altrimenti non fare nulla".
Enderland,

6

Il segno più probabilmente ha senso contestualmente in più casi, ma un contro-esempio (forse un'eccezione che dimostra la regola) in Python è l'oggetto set, che prevede -ma non +:

>>> 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'

Non ha senso usare il +segno perché l'intenzione potrebbe essere ambigua - significa impostare intersezione o unione? Invece, usa |per l'unione e &per l'intersezione:

>>> set('abc') | set('bcd')
set(['a', 'c', 'b', 'd'])
>>> set('abc') & set('bcd')
set(['c', 'b'])

2
Ciò è più probabile perché la sottrazione impostata è definita in matematica, ma non l'aggiunta impostata.
Mehrdad,

L'uso di "-" sembra complicato; ciò che è veramente necessario è un operatore "ma non" che sarebbe utile anche quando si esegue l'aritmetica bit a bit con numeri interi. Se 30 ~ e 7 fossero 24, l'utilizzo di ~ & con i set si adatterebbe perfettamente con & e | anche se i set mancano di un operatore ~.
supercat

1
set('abc') ^ set('bcd')ritorna set(['a', 'd']), se stai chiedendo della differenza simmetrica.
Aaron Hall,

3

" -" è usato in alcune parole composte (ad esempio "sul posto") per unire le diverse parti nella stessa parola. Perché non utilizziamo " -" per unire stringhe diverse nei linguaggi di programmazione? Penso che avrebbe perfettamente senso! Al diavolo questa +assurdità!

Tuttavia, proviamo a guardare questo da un angolo un po 'più astratto.

Come definiresti l'algebra delle stringhe? Quali operazioni vorresti avere e quali leggi valerebbero per loro? Quali sarebbero le loro relazioni?

Ricorda, potrebbe non esserci assolutamente alcuna ambiguità! Ogni possibile caso deve essere ben definito, anche se ciò significa che non è possibile farlo! Più piccola è la tua algebra, più è facile farlo.

Ad esempio, cosa significa effettivamente aggiungere o sottrarre due stringhe?

Se aggiungi due stringhe (ad esempio, let a = "aa"e b = "bb"), otterrai aabbcome risultato a + b?

Che ne dici b + a? Sarebbe bbaa? Perché no aabb? Cosa succede se sottrai aadal risultato della tua aggiunta? La tua stringa avrebbe un concetto di quantità negativa aain essa?

Ora torna all'inizio di questa risposta e sostituisci spaceshuttleinvece della stringa. In generale, perché viene definita o non definita alcuna operazione per qualsiasi tipo?

Il punto che sto cercando di sottolineare è che non c'è nulla che ti impedisca di creare un'algebra per qualsiasi cosa. Potrebbe essere difficile trovare operazioni significative o persino operazioni utili per questo.

Per quanto riguarda le stringhe, la concatenazione è praticamente l'unica sensata che abbia mai incontrato. Non importa quale simbolo viene utilizzato per rappresentare l'operazione.


1
"Per gli archi, il concatenamento è praticamente l'unico sensato che abbia mai incontrato" . Allora non sei d'accordo con Python 'xy' * 3 == 'xyxyxy'?
smci,

3
@smci che è solo una moltiplicazione come aggiunta ripetuta , sicuramente?
jonrsharpe,

qual è l'operatore corretto per concatenare le navicelle spaziali?
Mr.Mindor,

4
@ Mr.Mindor backspace ... per rimuovere lo spazio tra le navicelle spaziali.
Young John,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.