Neden '-' eksi işareti genellikle artı işaretiyle aynı şekilde aşırı yüklenmiyor?


64

Artı işareti +, toplama ve string bitiştirme için kullanılır, fakat onun arkadaşı: eksi işareti, -genellikle dizelerin kırpılması veya çıkarmadan başka bir durum için görülür. Bunun nedeni veya sınırlamaları ne olabilir?

JavaScript'te aşağıdaki örneği göz önünde bulundurun:

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

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

35
hangi "yy" kaldırılmalıdır?
gashach

12
'+' İşaretinin davranışına girersem, o zaman en doğru olanı mantıklı kılar.
Digvijay Yadav

46
İkili +operatörün tamamen ilgisiz iki anlamı olan “sayısal ekleme” ve “dizi birleştirme” ile aşırı yüklenmesi yeterince kötü . Neyse ki, bazı diller gibi ayrı bir birleştirme operatörü sağlamak .(Perl5, PHP), ~(Perl6), &(VB), ++... (Haskell),
amon

6
@MasonWheeler Kullanırlar ->(sanal yöntem çağrıları mutlaka işaretçi benzeri yönlendirmeyi içerdiğinden C üyeliğinin erişimini kaldırmayı düşünün). Daha .yaygın bir kongre olmasına rağmen, bir operatörü kullanmak için yöntem çağrıları / üye erişimi gerektiren bir dil tasarımı yasası yoktur . Smalltalk'in yöntem çağrısı operatörü olmadığını biliyor muydunuz? Basit bir araya getirme object methodyeterlidir.
amon

20
Python , ayarlanmış çıkarma için eksi yükler yapar (ve kullanıcı tanımlı türlerde de aşırı yüklenebilir). Python setleri ayrıca, kesişme / birleştirme / vb. İçin bitsel operatörlerin çoğunu aşırı yükler.
Kevin,

Yanıtlar:


116

Kısacası, insanların algoritmalar yazmak istedikleri teller üzerinde özellikle faydalı çıkarmaya benzer işlemler yoktur.

+Operatör, genellikle bir katkı maddesi işlemini gösterir Monoid bu, bir kimlik elemanı ile bir birleştirici bir işlemdir:

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

Tamsayı toplama, dizi bitiştirme ve set birliği gibi şeyler için bu işleci kullanmak mantıklıdır, çünkü hepsi aynı cebirsel yapıya sahiptir:

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

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

Ve bunu, concatherhangi bir "birleştirilebilir" şey dizisi üzerinde çalışan bir işlev gibi kullanışlı algoritmalar yazmak için kullanabiliriz , örneğin:

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

Çıkarma işlemi gerçekleştiğinde -, genellikle her eleman A için ters −A ekleyen bir grubun yapısı hakkında konuşursunuz , böylece:

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

Ve bu tamsayı ve kayan nokta çıkarma, hatta fark yaratma gibi şeyler için mantıklı olsa da, dizeler ve listeler için pek mantıklı gelmiyor. Bunun tersi nedir "foo"?

Bir iptal monoid denilen , tersine sahip olmayan ancak iptal etme özelliğine sahip bir yapı var , öyle ki:

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

Bu tanımladığınız "ab" - "b" == "a"ancak tanımlanmadığınız, tanımladığınız yapıdır "ab" - "c". Sadece bu yapıyı kullanan çok sayıda algoritmaya sahip değiliz. Sanırım seri hale getirme olarak birleştirmeyi düşünüyorsanız, çıkarma bir tür ayrıştırma için kullanılabilir.


2
Kümeler (ve çok kümeler) için çıkarma işlemi mantıklıdır, çünkü dizilerin aksine, öğenin sırası önemli değildir.
CodesInChaos

@CodesInChaos: Onlardan bahsettim, ama kümeleri grup olarak örneklendirmek konusunda gerçekten rahat değildim - genellikle kümenin tersini oluşturamayacağınız için bir tane oluşturduklarına inanmıyorum.
Jon Purdy

12
Aslında, +işlem aynı zamanda sayılar için değişimlidir, yani A+B == B+Aonu dize birleştirme için kötü bir aday yapar . Bu, ayrıca kafa karıştırıcı operatör önceliği +dize bitiştirmeyi tarihsel bir hata kullanarak kullanır . Ancak, -hangi string işlemi kullanmanın işleri daha da kötüleştirdiği doğrudur …
Holger

2
@Darkhogg: Doğru! PHP .Perl'den ödünç alındı; öyle ~, Perl6 muhtemelen diğerleri.
Jon Purdy

1
@MartinBeckett, ancak davranışların kafa karıştırıcı olabileceğini görebilirsiniz .text.gz.text...
Örümcek Boris,

38

Çünkü herhangi iki geçerli dizenin birleştirilmesi her zaman geçerli bir işlemdir, ancak bunun tersi doğru değildir.

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

a - bBurada ne olmalı ? Bu soruyu cevaplamanın gerçekten iyi bir yolu yok, çünkü sorunun kendisi geçerli değil.


31
@DigvijayYadav, 5 elmayı 5 elmayı kaldırırsanız, o zaman -5 tane mango tezgahı olmalı mı? Hiçbir şey yapmaz mı? Bunu, bu operatörü bu şekilde kullanmak için tüm dilleri derleyenlere ve tercümanlara kabul edilebilecek ve tanımlayabilecek kadar iyi tanımlayabilir misiniz? Buradaki en büyük zorluk bu.
JB King,

28
@DigvijayYadav: Yani, bunu uygulamak için iki olası yolu tanımladınız ve her birini geçerli olarak kabul etmek için iyi bir argüman var, bu yüzden bu işlemi belirtme fikrini şimdiden karıştırdık. : P
Mason Wheeler

13
@smci Bana 5 + Falsegöre bir hata bir boolean ve bir boolean bir sayı olmadığı için bir hata olmalı .
Mason Wheeler

6
@JanDvorak: Bu konuda özellikle "Haskelly" diye bir şey yok; bu temel güçlü yazımdır.
Mason Wheeler

5
@DigvijayYadav Yani (a+b)-b = a(umarım!), Fakat (a-b)+bbazen a, bazen a+bise bağlı bde bir alt aya da değil? Bu ne çılgınlığı?

28

Çünkü -karakter dizisi manipülasyonu için yeterli "anlamsal uyum" yok. Operatörler, yalnızca aşırı yükün operands ile ne yaptığını açıkça belirttiğinde aşırı yüklenmelidir ve string çıkarma işlemi bu çubuğu karşılamaz.

Sonuç olarak, yöntem çağrıları tercih edilir:

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

C # dilinde, +form birleştirme için kullanıyoruz çünkü form

var result = string1 + string2 + string3;

onun yerine

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

Bir işlev çağrısı, anlamsal bir bakış açısıyla muhtemelen daha "doğru" olmasına rağmen, kullanımı kolay ve tartışmalı olarak daha kolaydır.

+Operatör gerçekten sadece bu bağlamda bir anlamı olabilir. Bu gibi doğru değildir -çıkarılarak dizeleri kavramı belirsiz (işlev çağrısı olduğundan, Replace(source, oldValue, newValue)ile ""olduğu gibi newValueparametresi tüm şüphe kaldırır ve fonksiyon değil, sadece bunları kaldırmak alt dizeleri değiştirmek için kullanılabilir).

Elbette ki sorun, operatörün aşırı yüklenmesinin, operatöre geçen tiplere bağlı olmasıdır ve bir sayının olması gereken bir dize geçirirseniz, beklemeyeceğiniz bir sonuç alabilirsiniz. Ek olarak, birçok birleştirme için (yani bir döngüde), bir StringBuildernesnenin kullanılması tercih edilir, çünkü her kullanım +yepyeni bir dize oluşturur ve performans zarar görebilir. Yani +operatör tüm bağlamlarda uygun değil.

İp birleştirme için +operatörden daha iyi bir semantik yapışkanlığa sahip operatör aşırı yüklemeleri vardır . İşte iki karmaşık sayı ekleyen:

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

8
+1 A ve B şeklinde iki dize verildiğinde, AB'yi "A'nın sonundan izleyen bir B'yi kaldır", "" B'nin bir örneğini A'daki bir yerden kaldır, "" B'nin tüm örneklerini A'daki bir yerden kaldır , "veya hatta" B'de bulunan tüm karakterleri A'dan kaldır ".
Cort Ammon

8

Groovy dili izin vermez -:

println('ABC'-'B')

döner:

AC

Ve:

println( 'Hello' - 'World' )

döner:

Hello

Ve:

println('ABABABABAB' - 'B')

döner:

AABABABAB

11
İlginç - bu yüzden ilk oluşumunu kaldırmayı seçer? Tamamen karşı sezgisel davranış için iyi bir örnek.
Hulk

9
Dolayısıyla, ('ABABABABA' + 'B') - 'B'başlangıç ​​değeriyle aynı yere yakın bir yerimiz yok 'ABABABABA'.
CVn

3
@ MichaelKjörling OTOH, (A + B) - A == Bher A ve B için. Buna sola çıkarma diyebilir miyim?
John Dvorak

2
Haskell ++birleştirme için var. Herhangi bir listede çalışır ve bir dize sadece bir karakter listesidir. Ayrıca \\, sağ argümandaki her bir öğenin ilk oluşumunu sol argümandan kaldıran da vardır .
John Dvorak

3
Bu örneklerin tam olarak neden dizgiler için eksi operatör olmamasının gerekli olduğunu hissediyorum. Tutarsız ve sezgisel olmayan davranış. "-" Kesinlikle sanmıyorum "derken, eşleşen dizenin ilk örneğini kaldırın, eğer gerçekleşirse, sadece hiçbir şey yapmayın."
enderland

6

Artı işareti muhtemelen bağlamsal olarak daha fazla durumda anlamlıdır, ancak Python'daki bir karşı örnek (belki de kuralı ispatlayan bir istisna), aşağıdakileri sağlayan -ancak vermeyen set nesnesidir +:

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

Bu +işareti kullanmak mantıklı değil çünkü niyet belirsiz olabilir - bu kesişme veya sendika kurmak anlamına mı geliyor? Bunun yerine, |birlik ve &kavşak için kullanır :

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

2
Bu daha olasıdır çünkü küme çıkarma matematikte tanımlanır, fakat küme toplama değildir.
Mehrdad

"-" kullanımı tehlikeli görünüyor; gerçekte ihtiyaç duyulan şey tamsayılarla bitsel aritmetik işlem yaparken de yararlı olacak bir "ama değil" operatörüdür. 30 ~ & 7, 24 olsaydı, o zaman ~ & kümeleriyle kullanmak, ve & | Her ne kadar kümelerde operatör yoksa.
supercat

1
set('abc') ^ set('bcd')set(['a', 'd'])simetrik fark hakkında soruyorsanız döner .
Aaron Hall

3

" -Aynı kelime, farklı parçaların birleştirilmesi için" ( "yerinde, örneğin) bir bileşik, bir deyişle kullanılan". Neden -farklı dizeleri programlama dillerinde birleştirmek için " " kullanmıyoruz ? Bence bu çok mantıklı olur! Bu +saçmalığı cehenneme !

Ancak, buna biraz daha soyut bir açıdan bakmayı deneyelim.

String cebirini nasıl tanımlarsın? Hangi işlemleri yapardınız ve onlar için hangi yasaları tutarsınız? İlişkileri ne olurdu?

Unutma, kesinlikle hiçbir belirsizlik olabilir! Bunu yapmanın mümkün olmadığı anlamına gelse bile, her olası dava iyi tanımlanmalıdır! Cebiriniz ne kadar küçük olursa, bu o kadar kolay yapılır.

Örneğin, iki dize eklemek veya çıkarmak ne anlama geliyor?

İki dize eklerseniz (örneğin, izin ver a = "aa"ve b = "bb"), aabbsonucunun sonucu olur a + bmu?

Nasıl hakkında b + a? Bu olur bbaamu? Neden olmasın aabb? aaEklemenizin sonucundan çıkarırsanız ne olur ? İpinizde negatif miktarda bir kavram var aamıydı?

Şimdi bu cevabın başlangıcına geri dönün ve spaceshuttleyerine dize kullanın. Genellemek için, herhangi bir işlem neden herhangi bir tür için tanımlanmış veya tanımlanmamış?

Yapmaya çalıştığım nokta şu ki, herhangi bir şey için cebir yaratmanı engelleyen hiçbir şey yok. Anlamlı işlemler veya bunun için faydalı işlemler bulmak zor olabilir.

Dizeler için, birleştirme, şimdiye kadar karşılaştığım en mantıklı olanı. İşlemi temsil etmek için hangi sembolün kullanıldığı önemli değildir.


1
"Dizgiler için, birleştirme, şimdiye kadar karşılaştığım tek mantıklı" . Sonra Python en katılmıyorusunuz 'xy' * 3 == 'xyxyxy'?
smci

3
@smci bu sadece tekrarlanan-olarak-toplama olarak çarpma , kesinlikle?
jonrsharpe

uzay uzaylarını birleştirmek için uygun operatör nedir?
Mr.Mindor

4
Boşluklar arasındaki boşluğu kaldırmak için Bay Mr.
YoungJohn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.