Por que o sinal de menos, '-', geralmente não está sobrecarregado da mesma maneira que o sinal de mais?

64

O sinal de mais +é usado para adição e concatenação de cadeias, mas seu companheiro: o sinal de menos -, geralmente não é visto para aparar cadeias ou algum outro caso que não seja a subtração. Qual poderia ser o motivo ou as limitações para isso?

Considere o seguinte exemplo em JavaScript:

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

a-b == NaN
// but
a+b == "abcdefgefg"
Digvijay Yadav
fonte
35
qual "aa" deve ser removido?
gashach
12
Se eu seguir o comportamento do sinal '+', o mais correto faz sentido.
Digvijay Yadav
46
Já é ruim o suficiente que o +operador binário seja sobrecarregado com os dois significados totalmente independentes: "adição numérica" ​​e "concatenação de string". Felizmente, algumas linguagens fornecem um operador de concatenação separado, como .(Perl5, PHP), ~(Perl6), &(VB), ++(Haskell),…
amon
6
@MasonWheeler Eles usam ->(pense em remover o acesso de membro em C, já que as chamadas de método virtual envolvem necessariamente indireto como ponteiro). Não há lei do design de idiomas que exija chamadas de método / acesso de membro para usar um .operador, embora seja uma convenção cada vez mais comum. Você sabia que o Smalltalk não tem operador de chamada de método? A justaposição simples object methodé suficiente.
amon
20
Python faz sobrecarga menos, para o conjunto de subtracção (e ele pode ser sobrecarregado em tipos definidos pelo utilizador, bem). Os conjuntos Python também sobrecarregam a maioria dos operadores bit a bit para interseção / união / etc.
28415 Kevin

Respostas:

116

Em resumo, não existem operações particularmente úteis de subtração em strings com as quais as pessoas desejam escrever algoritmos.

O +operador geralmente denota a operação de um monóide aditivo , ou seja, uma operação associativa com um elemento de identidade:

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

Faz sentido usar esse operador para coisas como adição de número inteiro, concatenação de strings e união de conjuntos, pois todos eles têm a mesma estrutura algébrica:

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

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

E podemos usá-lo para escrever algoritmos úteis, como uma concatfunção que funciona em uma sequência de qualquer coisa "concatenável", por exemplo:

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

Quando a subtração -é envolvida, você geralmente fala sobre a estrutura de um grupo , que adiciona um −A inverso para cada elemento A, de modo que:

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

E enquanto isso faz sentido para coisas como subtração de números inteiros e de ponto flutuante, ou até mesmo definir diferenças, não faz muito sentido para seqüências de caracteres e listas. Qual é o inverso de "foo"?

Existe uma estrutura chamada monóide cancelável , que não possui inversões, mas possui a propriedade cancelamento , de modo que:

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

Essa é a estrutura que você descreve, onde "ab" - "b" == "a", mas "ab" - "c"não está definida. Só que não temos muitos algoritmos úteis que usam essa estrutura. Eu acho que se você pensa em concatenação como serialização, a subtração pode ser usada para algum tipo de análise.

Jon Purdy
fonte
2
Para conjuntos (e multi-conjuntos), a subtração faz sentido, porque, diferentemente das seqüências, a ordem do elemento não importa.
CodesInChaos 28/10
@CodesInChaos: adicionei uma menção a eles, mas não me senti muito confortável colocando conjuntos como exemplo de grupo - não acredito que eles formem um, pois geralmente não é possível construir o inverso de um conjunto.
Jon Purdy
12
Na verdade, a +operação também é comutativa para números, ou seja A+B == B+A, o que a torna uma candidata ruim à concatenação de strings. Isso, mais a precedente confusa do operador, faz com que o uso +de concatenação de cadeias seja um erro histórico. No entanto, é verdade que o uso -por qualquer operação de corda feito coisas muito piores ...
Holger
2
@Darkhogg: Certo! PHP emprestado .do Perl; está ~em Perl6, possivelmente outros.
21415 Jon Purdy
11
@MartinBeckett, mas você pode ver que o comportamento pode ser confuso com .text.gz.text...
Boris the Spider
38

Como a concatenação de duas seqüências válidas é sempre uma operação válida, mas o oposto não é verdadeiro.

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

O que deveria a - bestar aqui? Realmente não há uma boa maneira de responder a essa pergunta, porque a pergunta em si não é válida.

Mason Wheeler
fonte
31
@DigvijayYadav, se você remover 5 mangas de 5 maçãs, deve haver um contador de -5 mangas? Isso não faz nada? Você pode definir isso suficientemente bem para que possa ser amplamente aceito e colocado em todos os compiladores e intérpretes de idiomas para usar este operador neste formulário? Esse é o grande desafio aqui.
JB rei
28
@DigvijayYadav: Então você acabou de descrever duas maneiras possíveis de implementar isso, e há um bom argumento para considerar cada uma como válida, então já estamos fazendo uma bagunça com a idéia de especificar esta operação. : P
Mason Wheeler
13
@smci Parece-me 5 + Falseobviamente que deve ser um erro , pois um número não é booleano e um booleano não é um número.
Mason Wheeler
6
@JanDvorak: Não há nada particularmente "Haskelly" nisso; isso é digitação forte básica.
Mason Wheeler
5
@DigvijayYadav Então (a+b)-b = a(espero!), Mas (a-b)+bàs vezes a, às vezes , a+bdependendo de se bé uma substring aou não? Que loucura é essa?
28

Porque o -operador para manipulação de strings não possui "coesão semântica" suficiente. Os operadores só devem ser sobrecarregados quando estiver absolutamente claro o que a sobrecarga faz com seus operandos, e a subtração de strings não atende a essa barra.

Conseqüentemente, as chamadas de método são preferidas:

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

Na linguagem C #, usamos +para concatenação de cadeias porque o formulário

var result = string1 + string2 + string3;

ao invés de

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

é conveniente e sem dúvida mais fácil de ler, mesmo que uma chamada de função seja provavelmente mais "correta", do ponto de vista semântico.

O +operador pode realmente significar apenas uma coisa nesse contexto. Isso não é verdade -, pois a noção de subtração de strings é ambígua (a chamada de função Replace(source, oldValue, newValue)com ""o newValueparâmetro remove todas as dúvidas e a função pode ser usada para alterar substrings, não apenas removê-los).

O problema, é claro, é que a sobrecarga do operador depende dos tipos que estão sendo passados ​​para o operador e, se você passar uma sequência em que deveria estar um número, poderá obter um resultado que não esperava. Além disso, para muitas concatenações (ou seja, em um loop), um StringBuilderobjeto é preferível, pois cada uso +cria uma nova string e o desempenho pode sofrer. Portanto, o +operador nem sequer é apropriado em todos os contextos.

Existem sobrecargas de operador que possuem melhor coesão semântica do que o +operador para concatenação de cadeias. Aqui está um que adiciona dois números complexos:

public static Complex operator +(Complex c1, Complex c2) 
{
    return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}
Robert Harvey
fonte
8
+1 Dadas duas seqüências de caracteres, A e B, posso pensar em AB como "remover um B à direita do final de A", "remover uma instância de B de algum lugar em A" "" remover todas as instâncias de B de algum lugar em A , "ou mesmo" remova todos os caracteres encontrados em B de A. "
Cort Ammon
8

A linguagem Groovy permite -:

println('ABC'-'B')

retorna:

AC

E:

println( 'Hello' - 'World' )

retorna:

Hello

E:

println('ABABABABAB' - 'B')

retorna:

AABABABAB
Wim Deblauwe
fonte
11
Interessante - por isso escolhe remover a primeira ocorrência? Um bom exemplo para um comportamento completamente contra-intuitivo.
Hulk
9
Portanto, temos que ('ABABABABA' + 'B') - 'B'não é nem de longe o mesmo que o valor inicial 'ABABABABA'.
um CVn
3
@ MichaelKjörling OTOH, (A + B) - A == Bpara todos os A e B. Posso chamar isso de subtração esquerda?
John Dvorak
2
Haskell tem ++para concatenação. Funciona em qualquer lista e uma string é apenas uma lista de caracteres. Também possui \\, o que remove a primeira ocorrência de cada elemento no argumento da direita do argumento da esquerda.
John Dvorak
3
Eu sinto que esses exemplos são exatamente o motivo pelo qual não deve haver operador negativo para seqüências de caracteres. É um comportamento inconsistente e não intuitivo. Quando penso em "-" com certeza não penso ", remova a primeira instância da sequência correspondente, se ocorrer, caso contrário, não faça nada".
Enderland
6

O sinal de mais provavelmente faz sentido contextualmente em mais casos, mas um contra-exemplo (talvez uma exceção que comprove a regra) no Python é o objeto set, que fornece, -mas não +:

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

Não faz sentido usar o +sinal porque a intenção pode ser ambígua - isso significa definir interseção ou união? Em vez disso, ele usa |para união e &interseção:

>>> set('abc') | set('bcd')
set(['a', 'c', 'b', 'd'])
>>> set('abc') & set('bcd')
set(['c', 'b'])
Aaron Hall
fonte
2
Isso é mais provável porque a subtração de conjunto é definida em matemática, mas a adição de conjunto não é.
Mehrdad
O uso de "-" parece desonesto; o que é realmente necessário é um operador "mas não" que também seria útil ao executar aritmética bit a bit com números inteiros. Se 30 ~ & 7 fossem 24, o uso de ~ & com conjuntos se encaixaria perfeitamente com & e | mesmo que os conjuntos não tenham um operador ~.
supercat
11
set('abc') ^ set('bcd')retorna set(['a', 'd']), se você estiver perguntando sobre a diferença simétrica.
Aaron Hall
3

" -" é usado em algumas palavras compostas (por exemplo, "no local") para unir as diferentes partes na mesma palavra. Por que não usamos " -" para unir diferentes strings em linguagens de programação? Eu acho que faria todo o sentido! Para o inferno com esse +absurdo!

No entanto, vamos tentar olhar para isso de um ângulo um pouco mais abstrato.

Como você definiria álgebra de cordas? Quais operações você teria e quais leis seriam válidas para eles? Quais seriam suas relações?

Lembre-se, pode não haver absolutamente nenhuma ambiguidade! Todo caso possível deve ser bem definido, mesmo que isso signifique dizer que não é possível fazer isso! Quanto menor a sua álgebra, mais fácil isso é feito.

Por exemplo, o que realmente significa adicionar ou subtrair duas strings?

Se você adicionar duas strings (por exemplo, let a = "aa"e b = "bb"), obteria aabbo resultado de a + b?

Que tal b + a? Seria isso bbaa? Por que não aabb? O que acontece se você subtrair aao resultado da sua adição? Sua string teria um conceito de quantidade negativa aa?

Agora volte ao início desta resposta e substitua em spaceshuttlevez da sequência. Para generalizar, por que alguma operação está definida ou não está definida para qualquer tipo?

O que estou tentando enfatizar é que não há nada que o impeça de criar uma álgebra para qualquer coisa. Pode ser difícil encontrar operações significativas, ou mesmo operações úteis para isso.

Para strings, concatenar é praticamente o único que eu já vi. Não importa qual símbolo é usado para representar a operação.

Zavior
fonte
11
"Para strings, concatenar é praticamente o único que eu já vi" . Então você discorda do Python 'xy' * 3 == 'xyxyxy'?
smci 29/10
3
@smci isso é apenas multiplicação como adição repetida , com certeza?
jonrsharpe
qual é o operador adequado para concatenar as naves espaciais?
Mr.Mindor
4
@ Mr.Mindor backspace ... para remover o espaço entre as naves espaciais.
YoungJohn