Znak plus +
służy do dodawania i konkatenacji łańcuchów, ale jego towarzysz: znak minus -
, na ogół nie jest widoczny do przycinania łańcuchów lub w innych przypadkach innych niż odejmowanie. Jaki może być tego powód lub ograniczenia?
Rozważ następujący przykład w JavaScript:
var a = "abcdefg";
var b = "efg";
a-b == NaN
// but
a+b == "abcdefgefg"
coding-standards
history
operators
overload
Digvijay Yadav
źródło
źródło
+
operator binarny jest przeciążony dwoma całkowicie niepowiązanymi znaczeniami „dodawanie liczbowe” i „konkatenacja ciągów”. Na szczęście niektóre języki zapewniają osobny operator konkatenacji, taki jak.
(Perl5, PHP),~
(Perl6),&
(VB),++
(Haskell),…->
(myśl, że dereferencjonowanie dostępu członków w C, ponieważ wirtualne wywołania metod muszą koniecznie obejmować pośrednie wskaźniki). Nie ma prawa projektowania języka, które wymaga wywołań metod / dostępu członków do korzystania z.
operatora, chociaż jest to coraz bardziej powszechna konwencja. Czy wiesz, że Smalltalk nie ma operatora wywołań metod?object method
Wystarczy proste zestawienie .Odpowiedzi:
Krótko mówiąc, nie ma żadnych szczególnie użytecznych operacji odejmujących na łańcuchach, z którymi ludzie chcieliby pisać algorytmy.
+
Operator zwykle oznacza operację addytywnej monoid , czyli asocjacyjna współpracy z elementem tożsamości:Sensowne jest używanie tego operatora do takich rzeczy, jak dodawanie liczb całkowitych, łączenie łańcuchów i ustawianie unii, ponieważ wszystkie one mają tę samą strukturę algebraiczną:
I możemy go użyć do napisania przydatnych algorytmów, takich jak
concat
funkcja, która działa na sekwencji dowolnych „ możliwych do połączenia” rzeczy, np .:Kiedy w grę
-
wchodzi odejmowanie , zwykle mówisz o strukturze grupy , która dodaje odwrotne −A dla każdego elementu A, tak że:I chociaż ma to sens w przypadku odejmowania liczb całkowitych i zmiennoprzecinkowych, a nawet ustawiania różnicy, nie ma to większego sensu dla ciągów i list. Jaka jest odwrotność
"foo"
?Istnieje struktura zwana monoidem eliminującym , która nie ma odwrotności, ale ma właściwość anulowania , dzięki czemu:
Jest to struktura, którą opisujesz, gdzie
"ab" - "b" == "a"
, ale"ab" - "c"
nie jest zdefiniowana. Po prostu nie mamy wielu przydatnych algorytmów, które wykorzystują tę strukturę. Myślę, że jeśli traktujesz konkatenację jako serializację, to można zastosować odejmowanie do pewnego rodzaju analizy.źródło
+
operacja jest również przemienna dla liczbA+B == B+A
, co oznacza, że jest złym kandydatem do łączenia ciągów znaków. To, plus mylące pierwszeństwo operatorów sprawia, że użycie+
do konkatenacji ciągów jest historycznym błędem. Prawdą jest jednak, że użycie-
dowolnej operacji łańcuchowej znacznie pogorszyło sytuację….
od Perla; jest~
w Perl6, prawdopodobnie inne..text.gz.text
...Ponieważ łączenie dowolnych dwóch poprawnych ciągów jest zawsze prawidłową operacją, ale odwrotność nie jest prawdą.
Co powinno
a - b
tu być? Naprawdę nie ma dobrego sposobu na udzielenie odpowiedzi na to pytanie, ponieważ samo pytanie jest nieprawidłowe.źródło
5 + False
powinien to być błąd , ponieważ liczba nie jest liczbą logiczną, a wartość logiczna nie jest liczbą.(a+b)-b = a
(mam nadzieję!), Ale(a-b)+b
czasamia
, czasema+b
zależy od tego, czyb
jest to podciąg,a
czy nie? Co to za szaleństwo?Ponieważ
-
operator manipulacji ciągami nie ma wystarczającej „spójności semantycznej”. Operatory powinny być przeciążane tylko wtedy, gdy jest absolutnie jasne, co przeciążenie robi z operandami, a odejmowanie łańcucha nie spełnia tego paska.W związku z tym preferowane są wywołania metod:
W języku C # używamy
+
do konkatenacji ciągów, ponieważ formazamiast
jest wygodny i prawdopodobnie łatwiejszy do odczytania, mimo że wywołanie funkcji jest prawdopodobnie bardziej „poprawne” z semantycznego punktu widzenia.
W
+
tym kontekście operator może naprawdę oznaczać tylko jedną rzecz. To nie jest prawdziwe dla-
, ponieważ pojęcie odjęcia strun jest niejednoznaczna (wywołanie funkcjiReplace(source, oldValue, newValue)
z""
jakonewValue
parametr usuwa wszelkie wątpliwości, a funkcja może być używana do zmiany podciągi, a nie tylko je usunąć).Problem polega oczywiście na tym, że przeciążenie operatora zależy od typów przekazywanych do operatora, a jeśli przekażesz ciąg znaków w miejscu, w którym powinna być liczba, możesz uzyskać wynik, którego się nie spodziewałeś. Ponadto w przypadku wielu konkatenacji (tj. W pętli)
StringBuilder
preferowany jest obiekt, ponieważ każde użycie+
tworzy nowy ciąg, co może pogorszyć wydajność. Więc+
operator nie jest nawet odpowiedni we wszystkich kontekstach.Istnieją przeciążenia operatora, które mają lepszą spójność semantyczną niż
+
operator do konkatenacji łańcucha. Oto jedna, która dodaje dwie liczby zespolone:źródło
Język Groovy pozwala
-
:zwroty:
I:
zwroty:
I:
zwroty:
źródło
('ABABABABA' + 'B') - 'B'
to nie jest prawie tak samo jak wartość początkowa'ABABABABA'
.(A + B) - A == B
dla każdego A i B. Czy mogę to nazwać odjęciem od lewej?++
na konkatenację. Działa na dowolnej liście, a ciąg znaków to tylko lista znaków. Ma również\\
, która usuwa pierwsze wystąpienie każdego elementu w prawym argumencie z lewego argumentu.Znak plus prawdopodobnie kontekstowo ma sens w większej liczbie przypadków, ale kontrprzykładem (być może wyjątkiem potwierdzającym regułę) w Pythonie jest obiekt set, który zapewnia,
-
ale nie+
:Używanie
+
znaku nie ma sensu, ponieważ intencja może być niejednoznaczna - czy oznacza to ustawienie skrzyżowania czy zjednoczenia? Zamiast tego używa|
do zjednoczenia i&
skrzyżowania:źródło
set('abc') ^ set('bcd')
zwracaset(['a', 'd'])
, jeśli pytasz o różnicę symetryczną.„
-
” jest używany w niektórych słowach złożonych (na przykład „na miejscu”) do łączenia różnych części w to samo słowo. Dlaczego nie używamy „-
” do łączenia różnych ciągów znaków w językach programowania? Myślę, że to miałoby sens! Do diabła z tymi+
bzdurami!Spróbujmy jednak spojrzeć na to z nieco bardziej abstrakcyjnego punktu widzenia.
Jak zdefiniowałbyś algebrę łańcuchów? Jakie miałbyś operacje i jakie prawa by na nich obowiązywały? Jakie byłyby ich relacje?
Pamiętaj, że może nie być absolutnie żadnych dwuznaczności! Każdy możliwy przypadek musi być dobrze zdefiniowany, nawet jeśli oznacza to, że nie można tego zrobić! Im mniejsza jest algebra, tym łatwiej to zrobić.
Na przykład, co tak naprawdę oznacza dodawanie lub odejmowanie dwóch ciągów?
Jeśli dodasz dwa ciągi (na przykład let
a = "aa"
ib = "bb"
), czy uzyskaszaabb
wynik w wynikua + b
?Jak o
b + a
? To by byłobbaa
? Dlaczego nieaabb
? Co się stanie, jeśli odejmieszaa
od wyniku dodania? Czy twój łańcuch miałby pojęcie ujemnej ilościaa
?Teraz wróć do początku tej odpowiedzi i zamień
spaceshuttle
zamiast łańcucha. Uogólniając, dlaczego jakakolwiek operacja jest zdefiniowana lub nie zdefiniowana dla żadnego typu?Chodzi mi o to, że nic nie stoi na przeszkodzie, aby stworzyć algebrę do czegokolwiek. Znalezienie znaczących operacji lub nawet przydatnych operacji może być trudne.
W przypadku łańcuchów łączenie jest właściwie jedynym sensownym, z jakim się zetknąłem. Nie ma znaczenia, jakiego symbolu użyto do przedstawienia operacji.
źródło
'xy' * 3 == 'xyxyxy'
?