Pourquoi le signe moins, "-", n'est-il généralement pas surchargé de la même manière que le signe plus?


64

Le signe plus +est utilisé pour l’addition et pour la concaténation de chaînes, mais son compagnon: le signe moins,, -n’est généralement pas utilisé pour le rognage de chaînes ou pour un autre cas autre que la soustraction. Quelle pourrait être la raison ou les limites de cela?

Considérez l'exemple suivant en JavaScript:

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

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

35
quel "yy" devrait être enlevé?
gashach

12
Si je vais avec le comportement du signe '+', alors le plus à droite prend tout son sens.
Digvijay Yadav

46
Il est déjà assez grave que l' +opérateur binaire soit surchargé avec les deux significations totalement non liées «addition numérique» et «concaténation de chaîne». Heureusement, certaines langues offrent un opérateur de concaténation séparé tel que .(Perl5, PHP), ~(Perl6), &(VB), ++(Haskell), ...
amon

6
@MasonWheeler Ils utilisent ->(pensez à déréférencer l'accès des membres en C, car les appels de méthodes virtuelles impliquent nécessairement une indirection de type pointeur). Aucune loi de conception de langage n'oblige les appels de méthode / l'accès des membres à utiliser un .opérateur, bien que ce soit une convention de plus en plus courante. Saviez-vous que Smalltalk n'a pas d'opérateur d'appel de méthode? Une simple juxtaposition object methodsuffit.
amon

20
Python effectue la surcharge moins, pour la soustraction d’ensembles (et elle peut également être surchargée dans des types définis par l’utilisateur). Les ensembles Python surchargent également la plupart des opérateurs au niveau des bits pour intersection / union / etc.
Kevin

Réponses:


116

En bref, il n’ya pas d’opérations de soustraction sur les chaînes particulièrement utiles avec lesquelles les gens ont voulu écrire des algorithmes.

L’ +opérateur dénote généralement le fonctionnement d’un monoïde additif , c’est-à-dire une opération associative avec un élément d’identité:

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

Il est logique d'utiliser cet opérateur pour des opérations telles que l'addition d'entiers, la concaténation de chaînes et la définition de l'union, car elles ont toutes la même structure algébrique:

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

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

Et nous pouvons l’utiliser pour écrire des algorithmes pratiques, comme une concatfonction qui fonctionne sur une séquence de choses «concaténables», par exemple:

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

Lorsque la soustraction -est impliquée, vous parlez généralement de la structure d'un groupe , ce qui ajoute un inverse -A pour chaque élément A, de sorte que:

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

Et bien que cela ait un sens pour des choses comme la soustraction de nombres entiers et flottants, ou même pour la différence de valeurs, cela n'a pas beaucoup de sens pour les chaînes et les listes. Quel est l'inverse de "foo"?

Il existe une structure appelée monoïde d'annulation , qui n'a pas d'inverse, mais possède la propriété d' annulation , de sorte que:

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

C'est la structure que vous décrivez, où "ab" - "b" == "a", mais "ab" - "c"n'est pas définie. C'est juste que nous n'avons pas beaucoup d'algorithmes utiles qui utilisent cette structure. J'imagine que si vous envisagez la concaténation comme une sérialisation, la soustraction peut être utilisée pour une sorte d'analyse syntaxique.


2
La soustraction d'ensembles (et d'ensembles multiples) est logique, car contrairement aux séquences, l'ordre de l'élément n'a pas d'importance.
CodesInChaos

@CodesInChaos: J'ai ajouté une mention à leur sujet, mais je n'étais pas vraiment à l'aise de donner des ensembles comme exemple de groupe - je ne crois pas qu'ils en forment un, car on ne peut généralement pas construire l'inverse d'un ensemble.
Jon Purdy

12
En réalité, l' +opération est également commutative pour les nombres A+B == B+A, ce qui en fait un mauvais candidat pour la concaténation de chaînes. Ceci, ajouté à la priorité déroutante de l'opérateur, rend l'utilisation +d'une erreur historique pour la concaténation de chaînes. Cependant, il est vrai que l'utilisation -de n'importe quelle opération sur les cordes a rendu les choses bien pires…
Holger,

2
@Darkhogg: C'est ça! PHP emprunté .à Perl; c'est ~en Perl6, peut-être d'autres.
Jon Purdy

1
@MartinBeckett mais vous pouvez voir que le comportement peut être déroutant avec .text.gz.text...
Boris the Spider

38

Parce que la concaténation de deux chaînes valides est toujours une opération valide, mais l'inverse n'est pas vrai.

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

Que devrait a - bêtre ici? Il n'y a vraiment aucun moyen de répondre à cette question, car la question elle-même n'est pas valide.


31
@ DigvijayYadav, si vous retirez 5 mangues de 5 pommes, faut-il alors un compteur de -5 mangues? Cela ne fait-il rien? Pouvez-vous définir cela suffisamment bien pour qu’il puisse être accepté de manière large et intégré dans tous les compilateurs et interprètes de langues afin d’utiliser cet opérateur sous cette forme? C'est le grand défi ici.
JB King

28
@ DigvijayYadav: Donc, vous venez de décrire deux manières possibles d'implémenter cela, et il existe un bon argument pour considérer chacune d'elles comme étant valables. Nous faisons donc tout un bazot de l'idée de spécifier cette opération. : P
Mason Wheeler

13
@smci 5 + FalseIl me semble que cela devrait évidemment être une erreur , puisqu'un nombre n'est pas un booléen et qu'un booléen n'est pas un nombre.
Mason Wheeler

6
@JanDvorak: Il n'y a rien de particulièrement "Haskelly" à ce sujet; c'est typage fort de base.
Mason Wheeler

5
Alors @DigvijayYadav (a+b)-b = a(je l' espère!), Mais (a-b)+bparfois a, parfois a+bselon le cas best une sous - chaîne aou non? Quelle folie est-ce?

28

Parce que l' -opérateur de manipulation de chaîne n'a pas assez de "cohésion sémantique". Les opérateurs ne doivent être surchargés que lorsque la surcharge des opérandes est clairement définie et que la soustraction de chaînes ne respecte pas cette barre.

Par conséquent, les appels de méthode sont préférés:

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

En langage C #, nous utilisons +pour la concaténation de chaînes car la forme

var result = string1 + string2 + string3;

au lieu de

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

est pratique et peut-être plus facile à lire, même si un appel de fonction est probablement plus "correct", d’un point de vue sémantique.

L' +opérateur ne peut vraiment signifier qu'une chose dans ce contexte. Ce n'est pas aussi vrai -car la notion de soustraction de chaînes est ambiguë (l'appel de la fonction Replace(source, oldValue, newValue)avec ""comme newValueparamètre supprime tout doute, et la fonction peut être utilisée pour modifier des sous-chaînes, pas seulement pour les supprimer).

Le problème, bien sûr, est que la surcharge de l'opérateur dépend des types qui lui sont transmis. Si vous transmettez une chaîne où un nombre aurait dû être, vous risquez d'obtenir un résultat inattendu. En outre, pour de nombreuses concaténations (dans une boucle, par exemple), un StringBuilderobjet est préférable, car chaque utilisation de +crée une nouvelle chaîne et les performances peuvent en souffrir. Donc, l' +opérateur n'est même pas approprié dans tous les contextes.

Il existe des surcharges d'opérateurs qui ont une meilleure cohésion sémantique que l' +opérateur pour la concaténation de chaînes. En voici un qui ajoute deux nombres complexes:

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

8
+1 Étant donné deux chaînes, A et B, je peux penser à AB comme "supprime un B de fin à la fin de A", "supprime une instance de B de quelque part dans A", "supprime toutes les occurrences de B de quelque part dans A , "ou même" supprimer tous les caractères trouvés dans B de A. "
Cort Ammon

8

Le langage Groovy permet -:

println('ABC'-'B')

résultats:

AC

Et:

println( 'Hello' - 'World' )

résultats:

Hello

Et:

println('ABABABABAB' - 'B')

résultats:

AABABABAB

11
Intéressant - il choisit donc de supprimer la première occurrence? Un bon exemple de comportement totalement contre-intuitif.
Hulk

9
Par conséquent, nous avons ce qui ('ABABABABA' + 'B') - 'B'est loin d'être identique à la valeur de départ 'ABABABABA'.
un CVn

3
@ MichaelKjörling OTOH, (A + B) - A == Bpour chaque A et B. Puis-je appeler cela une soustraction à gauche?
John Dvorak

2
Haskell a ++pour concaténation. Cela fonctionne sur n'importe quelle liste et une chaîne n'est qu'une liste de caractères. Cela a également \\, qui supprime la première occurrence de chaque élément dans l'argument de droite de l'argument de gauche.
John Dvorak

3
J'ai l'impression que ces exemples sont exactement la raison pour laquelle il ne devrait pas y avoir d'opérateur moins pour les chaînes. C'est un comportement incohérent et non intuitif. Quand je pense à "-" Je ne pense vraiment pas, "supprime la première instance de la chaîne correspondante, si elle se produit, sinon ne fait rien."
Enderland

6

Le signe plus a probablement un sens contextuel dans un plus grand nombre de cas, mais un contre-exemple (peut-être une exception qui prouve la règle) en Python est l'objet set, qui fournit, -mais 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'

Il n’a pas de sens d’utiliser le +signe parce que l’intention peut être ambiguë. Est-ce que cela signifie une intersection ou une union? Au lieu de cela, il utilise |pour l'union et &pour l'intersection:

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

2
Cela est plus probable car la soustraction d’ensembles est définie en mathématiques, mais l’ajout d’ensembles ne l’est pas.
Mehrdad

L'utilisation de "-" semble risquée; ce qui est vraiment nécessaire, c'est un opérateur "mais pas" qui serait également utile pour effectuer une arithmétique au niveau des bits avec des entiers. Si 30 ~ et 7 avaient 24 ans, utiliser ~ et avec des ensembles irait bien avec & et | même si les ensembles manquent d'un opérateur ~.
Supercat

1
set('abc') ^ set('bcd')renvoie set(['a', 'd']), si vous vous interrogez sur la différence symétrique.
Aaron Hall

3

" -" est utilisé dans certains mots composés (par exemple, "sur site") pour joindre les différentes parties dans le même mot. Pourquoi n'utilisons-nous pas " -" pour joindre différentes chaînes dans les langages de programmation? Je pense que cela aurait un sens parfait! Au diable cette +absurdité!

Cependant, essayons de voir cela sous un angle un peu plus abstrait.

Comment définiriez-vous l'algèbre des cordes? Quelles opérations auriez-vous et quelles lois leur seraient applicables? Quelles seraient leurs relations?

Rappelez-vous, il n’ya peut-être aucune ambiguïté! Tous les cas possibles doivent être bien définis, même si cela signifie qu'il n'est pas possible de le faire! Plus votre algèbre est petite, plus cela est facile.

Par exemple, que signifie réellement ajouter ou soustraire deux chaînes?

Si vous ajoutez deux chaînes (par exemple, let a = "aa"et b = "bb"), obtiendriez-vous aabble résultat de a + b?

Que diriez- b + avous Serait-ce bbaa? Pourquoi ne pas aabb? Que se passe-t-il si vous soustrayez aadu résultat de votre addition? Votre chaîne aurait-elle un concept de quantité négative de aa?

Revenez maintenant au début de cette réponse et remplacez-la spaceshuttlepar la chaîne. Pour généraliser, pourquoi toute opération définie ou non définie pour un type quelconque?

Ce que j'essaie de dire, c'est que rien ne vous empêche de créer une algèbre. Il peut être difficile de trouver des opérations significatives, voire utiles.

Pour les cordes, la concaténation est à peu près la seule chose sensée que j'ai jamais rencontrée. Peu importe quel symbole est utilisé pour représenter l'opération.


1
"Pour les cordes, concaténer est à peu près le seul sensé que j'ai jamais rencontré" . Alors êtes-vous en désaccord avec Python 'xy' * 3 == 'xyxyxy'?
smci

3
@ smci c'est tout simplement la multiplication-comme-répété-addition , sûrement?
jonrsharpe

quel est l'opérateur approprié pour concaténer les navettes spatiales?
Mr.Mindor

4
@ M.Mindor backspace ... pour supprimer l'espace entre les navettes.
YoungJohn
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.