ทำไมเครื่องหมายลบ '-' โดยทั่วไปจะไม่โอเวอร์โหลดในลักษณะเดียวกับเครื่องหมายบวก


64

เครื่องหมายบวก+จะใช้สำหรับการเพิ่มและสำหรับการต่อสายอักขระ แต่สหายของมัน: เครื่องหมายลบ-, โดยทั่วไปจะไม่เห็นการตัดแต่งสตริงหรือกรณีอื่นนอกเหนือจากการลบ อะไรคือเหตุผลหรือข้อ จำกัด สำหรับสิ่งนั้น

พิจารณาตัวอย่างต่อไปนี้ใน JavaScript:

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

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

35
ควรลบ "yy" ใด
gashach

12
ถ้าฉันไปกับพฤติกรรมของเครื่องหมาย '+' สิ่งที่ถูกต้องที่สุด
Digvijay Yadav

46
มันไม่ดีพอที่ตัว+ดำเนินการไบนารีจะโอเวอร์โหลดด้วยความหมายที่ไม่เกี่ยวข้องทั้งหมดสองอย่างคือ "การเพิ่มตัวเลข" และ "การต่อสตริง" โชคดีที่บางภาษามีตัวดำเนินการเชื่อมต่อที่แยกต่างหากเช่น.(Perl5, PHP), ~(Perl6), &(VB), ++(Haskell), ...
amon

6
@MasonWheeler พวกเขาใช้->(คิดว่าการยกเลิกการเข้าถึงการเข้าถึงสมาชิกใน C เนื่องจากการเรียกเมธอดเสมือนจำเป็นต้องมีการชี้นำทางอ้อมเหมือนตัวชี้) ไม่มีกฎหมายการออกแบบภาษาที่ต้องมีการเรียกใช้เมธอด / การเข้าถึงสมาชิกเพื่อใช้.โอเปอเรเตอร์แม้ว่าจะเป็นวิธีการทั่วไปที่เพิ่มมากขึ้น คุณรู้หรือไม่ว่า Smalltalk ไม่มีผู้ดำเนินการเรียกใช้วิธีการ? การวางเคียงกันง่าย ๆobject methodก็เพียงพอแล้ว
amon

20
Python ทำการโอเวอร์โหลดลบสำหรับการลบการตั้งค่า (และสามารถโอเวอร์โหลดในประเภทที่ผู้ใช้กำหนดเช่นกัน) Python ตั้งค่าโอเปอเรเตอร์ bitwise ให้มากเกินไปสำหรับการตัดกัน / รวม / ฯลฯ
Kevin

คำตอบ:


116

ในระยะสั้นไม่มีการดำเนินการใด ๆ ที่คล้ายกับการลบในสตริงที่ผู้คนต้องการเขียนอัลกอริทึมด้วย

+ผู้ประกอบการโดยทั่วไปหมายถึงการทำงานของสารเติมแต่งหนังสือ , ที่อยู่, การดำเนินการเชื่อมโยงกับเอกลักษณ์องค์ประกอบ:

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

มันสมเหตุสมผลที่จะใช้ตัวดำเนินการนี้สำหรับสิ่งต่าง ๆ เช่นการบวกจำนวนเต็มการต่อสตริงและตั้งค่าการรวมเนื่องจากพวกเขาทั้งหมดมีโครงสร้างพีชคณิตเดียวกัน:

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

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

และเราสามารถใช้มันเพื่อเขียนอัลกอริธึมที่มีประโยชน์เช่นconcatฟังก์ชั่นที่ทำงานตามลำดับของสิ่งที่ "เชื่อมต่อกัน" เช่น:

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

เมื่อการลบ-เข้ามาเกี่ยวข้องคุณมักพูดถึงโครงสร้างของกลุ่มซึ่งจะเพิ่มค่าผกผัน −A สำหรับทุกองค์ประกอบ A เพื่อให้:

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

และในขณะนี้ทำให้รู้สึกถึงสิ่งต่าง ๆ เช่นการลบจำนวนเต็มและทศนิยมหรือแม้กระทั่งการตั้งค่าความแตกต่าง แต่ก็ไม่สมเหตุสมผลสำหรับสตริงและรายการ การผกผันของ"foo"คืออะไร?

มีโครงสร้างที่เรียกว่าmonoid cancellativeซึ่งไม่มี inverses แต่มีคุณสมบัติการยกเลิกดังนั้น:

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

นี่คือโครงสร้างที่คุณอธิบายที่"ab" - "b" == "a"แต่"ab" - "c"ไม่ได้กำหนดไว้ เป็นเพียงว่าเราไม่มีอัลกอริทึมที่มีประโยชน์มากมายที่ใช้โครงสร้างนี้ ฉันเดาว่าถ้าคุณคิดว่าการต่อข้อมูลเป็นอนุกรมแล้วการลบสามารถใช้ในการแยกวิเคราะห์ได้


2
สำหรับการลบเซต (และหลายชุด) นั้นสมเหตุสมผลเนื่องจากต่างจากลำดับลำดับขององค์ประกอบจึงไม่สำคัญ
CodesInChaos

@CodesInChaos: ฉันเพิ่มการกล่าวถึงพวกเขา แต่ฉันไม่สบายใจที่จะนำชุดมาใส่เป็นตัวอย่างของกลุ่ม - ฉันไม่เชื่อว่าพวกเขาจะสร้างชุดหนึ่งขึ้นมาเพราะคุณไม่สามารถสร้างชุดอินเวอร์สของชุดได้
Jon Purdy

12
ที่จริงแล้วการ+ดำเนินการยังเป็นตัวสลับสำหรับตัวเลขเช่นA+B == B+Aกันซึ่งทำให้ผู้สมัครที่ไม่ดีสำหรับการต่อสตริง สิ่งนี้รวมถึงความสำคัญของโอเปอเรเตอร์ที่ทำให้เกิดความสับสนในการใช้+สำหรับการต่อสตริงเข้าด้วยกันเป็นความผิดพลาดทางประวัติศาสตร์ อย่างไรก็ตามมันเป็นความจริงที่การใช้-งานการดำเนินการกับสตริงทำให้สิ่งเลวร้ายลงมาก ...
โฮล

2
@Darkhogg: ถูกต้อง! PHP ยืม.จาก Perl; มัน~อยู่ใน Perl6 อาจจะเป็นคนอื่น
Jon Purdy

1
@MartinBeckett แต่คุณจะเห็นว่าพฤติกรรมอาจจะทำให้เกิดความสับสนกับ.text.gz.text...
บอริสไปเดอร์

38

เนื่องจากการต่อสตริงที่ใช้ได้สองสตริงนั้นเป็นการดำเนินการที่ถูกต้องเสมอ แต่สิ่งที่ตรงกันข้ามนั้นไม่เป็นความจริง

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

สิ่งที่ควรa - bอยู่ที่นี่ ไม่มีวิธีที่ดีที่จะตอบคำถามนี้เพราะคำถามนั้นไม่ถูกต้อง


31
@DigvijayYadav ถ้าคุณลบ 5 มะม่วงจาก 5 แอปเปิ้ลจะต้องมีเคาน์เตอร์ -5 มะม่วง? มันไม่ทำอะไรเลยเหรอ? คุณสามารถนิยามสิ่งนี้ได้ดีพอที่จะยอมรับได้ในวงกว้างและใส่ลงในตัวแปลภาษาและตัวแปลภาษาทั้งหมดเพื่อใช้ตัวดำเนินการนี้ในแบบฟอร์มนี้หรือไม่? นั่นคือความท้าทายที่ยิ่งใหญ่ที่นี่
JB King

28
@DigvijayYadav: ดังนั้นคุณเพียงแค่อธิบายวิธีการที่เป็นไปได้สองวิธีในการดำเนินการนี้และมีข้อโต้แย้งที่ดีที่จะพิจารณาว่าแต่ละวิธีใช้งานได้ถูกต้อง : P
Mason Wheeler

13
@smci 5 + Falseเห็นได้ชัดว่าฉันควรจะเป็นข้อผิดพลาดเนื่องจากตัวเลขไม่ใช่บูลีนและบูลีนไม่ใช่ตัวเลข
Mason Wheeler

6
@JanDvorak: ไม่มีอะไรโดยเฉพาะ "Haskelly" เกี่ยวกับเรื่องนี้; นั่นคือการพิมพ์ที่แข็งแกร่งขั้นพื้นฐาน
Mason Wheeler

5
@DigvijayYadav ดังนั้น(a+b)-b = a(หวังว่า!) แต่(a-b)+bบางaครั้งบางครั้งก็a+bขึ้นอยู่กับว่าbเป็น substring aหรือไม่? นี่มันบ้าอะไรกันนะ?

28

เนื่องจากตัว-ดำเนินการสำหรับการจัดการสตริงไม่มี "semantic cohesion" เพียงพอ ผู้ประกอบการควรโอเวอร์โหลดเฉพาะเมื่อชัดเจนว่าโอเวอร์โหลดทำอะไรกับตัวถูกดำเนินการและการลบสตริงไม่ตรงกับแถบนั้น

ดังนั้นการเรียกเมธอดจึงเป็นที่ต้องการ:

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

ในภาษา C # เราใช้+สำหรับการต่อสตริงเนื่องจากรูปแบบ

var result = string1 + string2 + string3;

แทน

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

สะดวกและง่ายต่อการอ่านแม้ว่าการเรียกใช้ฟังก์ชันอาจจะ "ถูกต้อง" มากกว่าจากมุมมองเชิงความหมาย

+ผู้ประกอบการสามารถจริงๆเท่านั้นหมายถึงสิ่งหนึ่งในบริบทนี้ นี้ไม่เป็นความจริงสำหรับ-เนื่องจากความคิดของสตริงลบคลุมเครือ (สายงานReplace(source, oldValue, newValue)ด้วย""เป็นnewValueพารามิเตอร์ขจัดข้อสงสัยทั้งหมดและฟังก์ชั่นที่สามารถใช้ในการปรับเปลี่ยนสตริงไม่เพียง แต่เอาพวกเขา)

แน่นอนว่าปัญหาคือตัวดำเนินการโอเวอร์โหลดขึ้นอยู่กับประเภทที่ส่งผ่านไปยังตัวดำเนินการและหากคุณผ่านสตริงที่ควรมีตัวเลขคุณอาจได้รับผลลัพธ์ที่คุณไม่คาดคิด นอกจากนี้สำหรับการต่อข้อมูลหลาย ๆ อัน (เช่นในลูป) StringBuilderวัตถุจะดีกว่าเนื่องจากการใช้แต่ละครั้ง+จะสร้างสตริงใหม่และประสิทธิภาพอาจลดลง ดังนั้น+ผู้ประกอบการจึงไม่เหมาะสมในทุกบริบท

มีตัวดำเนินการโอเวอร์โหลดที่มีความต่อเนื่องของความหมายที่ดีกว่าตัว+ดำเนินการสำหรับการต่อข้อมูลสตริง นี่คือหมายเลขที่เพิ่มจำนวนเชิงซ้อนสองจำนวน:

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

8
+1 ด้วยสองสาย A และ B ฉันสามารถคิดว่า AB เป็น "ลบ B ต่อท้ายจากจุดสิ้นสุดของ A" "ลบอินสแตนซ์ของ B จากที่อื่นใน A" "ลบอินสแตนซ์ทั้งหมดของ B จากที่ใดก็ได้ใน A , "หรือแม้กระทั่ง" ลบอักขระทั้งหมดที่พบใน B จาก A "
Cort Ammon

8

ภาษา Groovyไม่อนุญาตให้-:

println('ABC'-'B')

ผลตอบแทน:

AC

และ:

println( 'Hello' - 'World' )

ผลตอบแทน:

Hello

และ:

println('ABABABABAB' - 'B')

ผลตอบแทน:

AABABABAB

11
ที่น่าสนใจ - ดังนั้นจึงเลือกที่จะลบการเกิดขึ้นครั้งแรก? ตัวอย่างที่ดีสำหรับพฤติกรรมตอบโต้โดยสิ้นเชิง
Hulk

9
ดังนั้นเรามีที่อยู่ตรงไหนใกล้เหมือนกับค่าเริ่มต้น('ABABABABA' + 'B') - 'B' 'ABABABABA'
CVn

3
@ MichaelKjörling OTOH (A + B) - A == Bสำหรับทุก ๆ A และ B ฉันจะเรียกมันว่าการลบซ้ายได้ไหม?
John Dvorak

2
Haskell มี++ไว้สำหรับการต่อเชื่อม มันทำงานได้ในรายการใด ๆ และสตริงเป็นเพียงรายการของตัวละคร นอกจากนี้ยังมี\\ซึ่งจะลบการเกิดขึ้นครั้งแรกของทุกองค์ประกอบในการโต้แย้งที่ถูกต้องจากการโต้แย้งด้านซ้าย
John Dvorak

3
ฉันรู้สึกว่าตัวอย่างเหล่านี้เป็นเหตุผลที่ไม่ควรมีตัวดำเนินการลบสำหรับสตริง มันไม่สอดคล้องกันและไม่ใช่พฤติกรรมที่เข้าใจง่าย เมื่อฉันคิดถึง "-" ฉันแน่ใจว่าไม่คิดว่า "ให้ลบอินสแตนซ์แรกของสตริงที่ตรงกันถ้าเกิดขึ้นไม่เช่นนั้นจะไม่ทำอะไรเลย"
enderland

6

เครื่องหมายบวกอาจมีความหมายตามบริบทในบางกรณี แต่ตัวอย่างเคาน์เตอร์ (อาจเป็นข้อยกเว้นที่พิสูจน์กฎ) ใน Python คือชุดวัตถุซึ่งมีให้-แต่ไม่+:

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

มันไม่สมเหตุสมผลที่จะใช้+เครื่องหมายเพราะความตั้งใจอาจคลุมเครือ - มันหมายถึงจุดตัดหรือสหภาพ แต่ใช้|สำหรับสหภาพและ&แยก:

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

2
สิ่งนี้มีโอกาสมากขึ้นเนื่องจากการลบชุดถูกกำหนดในคณิตศาสตร์ แต่การเพิ่มชุดไม่ใช่
Mehrdad

การใช้ "-" ดูเหมือนจะหลบหลีก สิ่งที่จำเป็นจริงๆคือตัวดำเนินการ "แต่ไม่ใช่" ซึ่งจะเป็นประโยชน์เมื่อทำการคำนวณทางคณิตศาสตร์ด้วยจำนวนเต็มด้วย ถ้า 30 ~ & 7 เป็น 24 การใช้ ~ & กับเซตน่าจะพอดีกับ & และ | แม้ว่าเซตจะไม่มีตัวดำเนินการ ~
supercat

1
set('abc') ^ set('bcd')ผลตอบแทนset(['a', 'd'])ถ้าคุณถามเกี่ยวกับความแตกต่างสมมาตร
แอรอนฮอลล์

3

" -" ใช้ในคำประสมบางคำ (เช่น "บนเว็บไซต์") สำหรับการรวมส่วนต่าง ๆ เป็นคำเดียวกัน ทำไมเราไม่ใช้ " -" เพื่อรวมสตริงต่างๆเข้าด้วยกันในภาษาการเขียนโปรแกรม? ฉันคิดว่ามันสมเหตุสมผลดี! ตกนรกด้วย+เรื่องไร้สาระนี้!

อย่างไรก็ตามลองดูจากมุมที่เป็นนามธรรมมากกว่านี้

คุณจะนิยามพีชคณิตสตริงอย่างไร คุณจะมีการดำเนินงานอะไรและมีกฎหมายอะไรบ้างสำหรับพวกเขา ความสัมพันธ์ของพวกเขาจะเป็นอย่างไร?

โปรดจำไว้ว่าอาจไม่มีความกำกวมอย่างแน่นอน! ทุกกรณีที่เป็นไปได้จะต้องมีการกำหนดอย่างดีแม้ว่ามันจะหมายความว่ามันเป็นไปไม่ได้ที่จะทำเช่นนี้! ยิ่งพีชคณิตของคุณเล็กลงเท่านี้ก็จะยิ่งง่ายขึ้นเท่านั้น

ตัวอย่างเช่นการเพิ่มหรือลบสองสตริงหมายความว่าอย่างไร

หากคุณเพิ่มสองสาย (ตัวอย่างเช่นให้a = "aa"และb = "bb") คุณจะได้รับaabbเป็นผลมาจากa + b?

แล้วไงb + aล่ะ นั่นจะเป็นbbaaอย่างไร ทำไมไม่aabb? จะเกิดอะไรขึ้นถ้าคุณลบออกaaจากผลการเพิ่มของคุณ สตริงของคุณจะมีแนวคิดเกี่ยวกับจำนวนลบaaหรือไม่?

ตอนนี้กลับไปที่จุดเริ่มต้นของคำตอบนี้และแทนที่spaceshuttleแทนสตริง หากต้องการพูดคุยทั่วไปเหตุใดจึงมีการกำหนดการทำงานหรือไม่ได้กำหนดไว้สำหรับประเภทใด ๆ

ประเด็นที่ฉันพยายามทำคือว่าไม่มีอะไรหยุดคุณจากการสร้างพีชคณิตเพื่ออะไร อาจเป็นการยากที่จะค้นหาการดำเนินการที่มีความหมายหรือแม้แต่การดำเนินการที่เป็นประโยชน์สำหรับมัน

สำหรับสตริงการต่อข้อมูลนั้นเป็นสิ่งที่มีเหตุผลอย่างเดียวที่ฉันเคยเจอ ไม่สำคัญว่าจะใช้สัญลักษณ์ใดเพื่อแสดงการดำเนินการ


1
"สำหรับสายเชื่อมโยงสวยมากเพียงคนเดียวที่เหมาะสมที่ฉันเคยเจอ" ถ้าอย่างนั้นคุณไม่เห็นด้วยกับงูใหญ่'xy' * 3 == 'xyxyxy'?
smci

3
@smci นั่นเป็นเพียงการคูณ - ซ้ำแล้วซ้ำอีกอย่างแน่นอน?
jonrsharpe

ผู้ประกอบการที่เหมาะสมในการเชื่อมยานอวกาศคืออะไร?
Mr.Mindor

4
@ Mr.Mindor backspace ... เพื่อลบช่องว่างระหว่างยานอวกาศ
YoungJohn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.