ทำไมทางลัดเช่น x + = y จึงถือว่าเป็นแนวปฏิบัติที่ดี


305

ฉันไม่รู้ว่าสิ่งเหล่านี้เรียกว่าอะไร แต่ฉันเห็นพวกเขาตลอดเวลา การใช้งาน Python นั้นมีลักษณะดังนี้:

x += 5x = x + 5เป็นสัญกรณ์ชวเลข

แต่ทำไมถึงถือว่าเป็นวิธีปฏิบัติที่ดี? ฉันเจอมันเกือบทุกเล่มหรือแบบฝึกหัดการเขียนโปรแกรมที่ฉันได้อ่านสำหรับ Python, C, Rและอื่น ๆ ฉันเข้าใจว่าสะดวกแล้วประหยัดการกดแป้นสามครั้งรวมถึงช่องว่าง แต่พวกเขาเสมอดูเหมือนจะเดินทางฉันขึ้นเมื่อฉันอ่านรหัสและอย่างน้อยในใจของฉันให้มันน้อยอ่านไม่

ฉันขาดเหตุผลบางอย่างที่ชัดเจนและชัดเจนเหล่านี้ถูกใช้ทั่วสถานที่หรือไม่


@EricLippert: C # จัดการสิ่งนี้ในลักษณะเดียวกับคำตอบยอดนิยมหรือไม่? มัน CLR ฉลาดจริงมีประสิทธิภาพมากขึ้นที่จะบอกว่าx += 5กว่าx = x + 5? หรือมันเป็นเพียงแค่น้ำตาลทรายตามที่คุณแนะนำ?
blesh

24
@blesh: รายละเอียดเล็ก ๆ ของวิธีที่หนึ่งแสดงการเพิ่มในซอร์สโค้ดมีผลกระทบต่อประสิทธิภาพของโค้ดที่เรียกใช้งานได้ซึ่งอาจเกิดขึ้นในปี 1970 มันไม่ใช่ตอนนี้อย่างแน่นอน การเพิ่มประสิทธิภาพคอมไพเลอร์เป็นสิ่งที่ดีและคุณมีความกังวลที่ใหญ่กว่านาโนวินาทีที่นี่หรือที่นั่น แนวคิดที่ว่าตัวดำเนินการ + = ได้รับการพัฒนา "เมื่อยี่สิบปีที่แล้ว" นั้นผิดพลาดอย่างเห็นได้ชัด ปลายเดนนิสริชชี่พัฒนา C จาก 1969 ถึง 1973 ที่ Bell Labs
Eric Lippert

1
ดูblogs.msdn.com/b/ericlippert/archive/2011/03/29/… (ดูส่วนที่สอง)
SLaks

5
โปรแกรมเมอร์ที่ใช้งานได้ส่วนใหญ่จะพิจารณาการฝึกฝนที่ไม่ดีนี้
Pete Kirkham

คำตอบ:


598

มันไม่ย่อท้อ

+=สัญลักษณ์ที่ปรากฏในภาษา C ในปี 1970 และ - มีความคิดที่ C ของ "แอสเซมสมาร์ท" สอดคล้องกับการเรียนการสอนและเครื่อง adressing โหมดที่แตกต่างกันอย่างชัดเจน

สิ่งต่าง ๆ เช่น " i=i+1", "i+=1"และ" ++i", ถึงแม้ว่าในระดับนามธรรมจะให้เอฟเฟกต์แบบเดียวกัน แต่ก็จะทำงานในระดับต่ำกับวิธีการทำงานของโปรเซสเซอร์ที่แตกต่างกัน

โดยเฉพาะนิพจน์ทั้งสามนั้นสมมติว่าiตัวแปรอยู่ในหน่วยความจำที่เก็บไว้ใน CPU register (ลองตั้งชื่อD- คิดว่ามันเป็น "ตัวชี้ไปยัง int") และALUของโปรเซสเซอร์ใช้พารามิเตอร์และส่งกลับผลลัพธ์ใน "แอคคูมูเลเตอร์" (เรียกมันว่าเอ - คิดว่ามันเป็น int)

ด้วยข้อ จำกัด เหล่านี้ (พบได้บ่อยมากในไมโครโปรเซสเซอร์ทั้งหมดจากช่วงเวลานั้น) การแปลน่าจะเป็นไปได้มากที่สุด

;i = i+1;
MOV A,(D); //Move in A the content of the memory whose address is in D
ADD A, 1;  //The addition of an inlined constant
MOV (D) A; //Move the result back to i (this is the '=' of the expression)

;i+=1;
ADD (D),1; //Add an inlined constant to a memory address stored value

;++i;
INC (D); //Just "tick" a memory located counter

วิธีแรกของการทำเช่นนี้เป็นสิ่งที่ไม่น่าพอใจ แต่มันก็เป็นเรื่องทั่วไปมากขึ้นเมื่อใช้งานกับตัวแปรแทนที่จะเป็นค่าคงที่ ( ADD A, BหรือADD A, (D+x)) หรือเมื่อแปลนิพจน์ที่ซับซ้อนมากขึ้น (พวกมันทั้งหมดต้มลงในการดำเนินการ และทำซ้ำจนกว่าข้อโต้แย้งทั้งหมดจะหมดไป)

ข้อที่สองเป็นเรื่องปกติมากขึ้นของ "state machine": เราไม่ได้ "ประเมินนิพจน์" อีกต่อไป แต่ "ปฏิบัติการค่า": เรายังคงใช้ ALU แต่หลีกเลี่ยงการเคลื่อนย้ายค่าโดยรอบเป็นผลลัพธ์ที่ได้รับอนุญาตให้แทนที่พารามิเตอร์ การเรียนการสอนประเภทนี้ไม่สามารถใช้ในกรณีที่จำเป็นต้องใช้การแสดงออกที่ซับซ้อนมากขึ้น: i = 3*i + i-2ไม่สามารถดำเนินการในสถานที่เนื่องจากiต้องใช้เวลามากขึ้น

สิบเอ็ดง่ายกว่า - ไม่ได้พิจารณาแนวคิดของ "การเพิ่ม" แต่ใช้วงจร "ดั้งเดิม" (ในแง่การคำนวณ) มากกว่าสำหรับตัวนับ คำแนะนำสั้นโหลดเร็วขึ้นและดำเนินการทันทีเนื่องจากเครือข่าย combinatorial จำเป็นต้องทำการติดตั้งเพิ่มใหม่เพื่อให้ตัวนับมีขนาดเล็กลงและเร็วกว่าแอดเดอร์ตัวเต็ม

ด้วยคอมไพเลอร์ร่วมสมัย (อ้างถึง C, โดยตอนนี้), การเปิดใช้งานการเพิ่มประสิทธิภาพคอมไพเลอร์, การโต้ตอบสามารถสลับตามความสะดวกสบาย, แต่ยังคงมีความแตกต่างทางแนวคิดในความหมาย

x += 5 วิธี

  • ค้นหาสถานที่ที่ระบุโดย x
  • เพิ่ม 5 เข้าไป

แต่x = x + 5หมายถึง:

  • ประเมิน x + 5
    • ค้นหาสถานที่ที่ระบุโดย x
    • คัดลอก x ไปยังแอคคูมูเลเตอร์
    • เพิ่ม 5 ไปยังตัวสะสม
  • เก็บผลลัพธ์ใน x
    • ค้นหาสถานที่ที่ระบุโดย x
    • คัดลอกสะสมไป

แน่นอนการเพิ่มประสิทธิภาพสามารถ

  • หาก "การค้นหา x" ไม่มีผลข้างเคียงทั้งสอง "การค้นหา" สามารถทำได้หนึ่งครั้ง (และ x กลายเป็นที่อยู่ที่เก็บไว้ในการลงทะเบียนตัวชี้)
  • สามารถคัดลอกทั้งสองชุดได้หากมีการใช้ ADD &xแทนการสะสม

จึงทำให้รหัสที่ดีที่สุดให้ตรงx += 5หนึ่ง

แต่สิ่งนี้สามารถทำได้ก็ต่อเมื่อ "การค้นหา x" ไม่มีผลข้างเคียง

*(x()) = *(x()) + 5;

และ

*(x()) += 5;

มีความแตกต่างทางความหมายเนื่องจากx()ผลข้างเคียง (การยอมรับว่าx()เป็นฟังก์ชั่นที่ทำสิ่งแปลก ๆ และส่งคืนint*) จะถูกสร้างขึ้นสองครั้งหรือครั้งเดียว

ความเท่าเทียมกันระหว่างx = x + yและx += yเป็นเพราะกรณีเฉพาะที่+=และ=นำไปใช้กับค่า l โดยตรง

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


สำหรับผู้ที่ชื่นชอบรายละเอียดเพิ่มเติม ...

CPU ทุกตัวมี ALU (หน่วยคำนวณเชิงตรรกะ) ซึ่งก็คือเครือข่าย combinatorial ซึ่งอินพุตและเอาต์พุตนั้น "เสียบ" กับรีจิสเตอร์และ / หรือหน่วยความจำขึ้นอยู่กับ opcode ของคำสั่ง

โดยทั่วไปแล้วการดำเนินการแบบไบนารีจะดำเนินการในฐานะ "ตัวดัดแปลงของการลงทะเบียนแบบสะสมพร้อมอินพุตที่นำมา" ที่ไหนสักแห่ง "ซึ่งบางที่สามารถ - ภายในขั้นตอนการสอนเอง ขมับ: เช่น ADD AB) - ภายในหน่วยความจำตามที่อยู่ที่กำหนดโดยการลงทะเบียน (ปกติของการดึงข้อมูลเช่น: ADD A (H)) - H ในกรณีนี้ทำงานเหมือนตัวชี้การประชุม

ด้วยรหัสเทียมนี้x += 5คือ

ADD (X) 5

ในขณะที่x = x+5เป็น

MOVE A (X)
ADD A 5
MOVE (X) A

นั่นคือ x + 5 ให้ชั่วคราวที่ได้รับมอบหมายในภายหลัง x += 5ทำงานโดยตรงกับ x

การใช้งานจริงขึ้นอยู่กับชุดคำสั่งจริงของโปรเซสเซอร์: หากไม่มีADD (.) copcode รหัสแรกจะกลายเป็นครั้งที่สอง: ไม่มีทาง

หากมีรหัสดังกล่าวและมีการเปิดใช้งานการเพิ่มประสิทธิภาพการแสดงออกที่สองหลังจากที่กำจัดการย้ายย้อนกลับและปรับการลงทะเบียน opcode กลายเป็นครั้งแรก


90
+1 สำหรับคำตอบเดียวที่อธิบายว่ามันใช้ในการจับคู่กับรหัสเครื่องที่ต่างกัน (และมีประสิทธิภาพมากขึ้น) ในสมัยก่อน
PéterTörök

11
@ KeithThompson นั้นเป็นความจริง แต่ก็ไม่สามารถปฏิเสธได้ว่าการชุมนุมมีอิทธิพลอย่างมากต่อการออกแบบภาษา C (และต่อมาภาษารูปแบบ C ทั้งหมด)
MattDavey

51
Erm, "+ =" ไม่ได้จับคู่กับ "inc" (จะจับคู่กับ "เพิ่ม"), "++" จับคู่กับ "inc"
เบรนแดน

11
เมื่อ "20 ปีก่อน" ฉันคิดว่าคุณหมายถึง "30 ปีที่แล้ว" และ BTW, COBOL มี C ตีอีก 20 ปีด้วย: เพิ่ม 5 ถึง X
JoelFan

10
ยิ่งใหญ่ในทฤษฎี ผิดในข้อเท็จจริง x86 ASM INC เพิ่มเพียง 1 ดังนั้นจึงไม่มีผลกับตัวดำเนินการ "เพิ่มและกำหนด" ที่กล่าวถึงที่นี่ (นี่จะเป็นคำตอบที่ดีสำหรับ "++" และ "-" ถึงแม้ว่า)
Mark Brackett

281

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

x = x + 5 เรียกใช้การประมวลผลทางจิตของ "take x เพิ่มห้าเข้าไปแล้วกำหนดค่าใหม่นั้นกลับไปที่ x"

x += 5 คิดได้ว่าเป็น "เพิ่ม x 5 เท่า"

ดังนั้นมันจึงไม่ได้จดชวเลขเพียงอย่างเดียว แต่จริงๆแล้วมันอธิบายการทำงานได้โดยตรงมากกว่า เมื่ออ่านโค้ดจำนวนมากมันง่ายกว่ามากที่จะเข้าใจ


32
+1 ฉันเห็นด้วยทั้งหมด ฉันเข้าสู่การเขียนโปรแกรมตั้งแต่ยังเป็นเด็กเมื่อความคิดของฉันอ่อนไหวได้ง่ายและx = x + 5ยังทำให้ฉันลำบากใจ เมื่อฉันเข้าสู่วิชาคณิตศาสตร์ในวัยต่อมามันรบกวนฉันมากยิ่งขึ้น การใช้x += 5นั้นมีความหมายมากกว่าและมีความหมายมากกว่าในการแสดงออก
พหุนาม

44
นอกจากนี้ยังมีกรณีที่ตัวแปรมีชื่อยาว: reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1... โอ้โห !!! พิมพ์ผิด

9
@ Matt Fenwick: ไม่จำเป็นต้องเป็นชื่อตัวแปรแบบยาว มันอาจเป็นนิพจน์ของการเรียงลำดับบางอย่าง สิ่งเหล่านี้มีแนวโน้มที่จะยืนยันได้ยากยิ่งขึ้นและผู้อ่านจะต้องให้ความสนใจอย่างมากเพื่อให้แน่ใจว่าพวกเขาเหมือนกัน
David Thornley

32
X = X + 5 คือการจัดเรียงสับเมื่อเป้าหมายของคุณคือเพื่อเพิ่ม x โดย 5
JeffO

1
@ โพลีโนเมียลฉันคิดว่าฉันเจอแนวคิดของ x = x + 5 เหมือนเด็กด้วย 9 ปีถ้าฉันจำได้อย่างถูกต้อง - และมันทำให้รู้สึกที่สมบูรณ์แบบสำหรับฉันและมันก็ยังทำให้รู้สึกที่สมบูรณ์แบบสำหรับฉันตอนนี้และชอบมันมากกว่า x + = 5 ข้อแรกคือ verbose มากขึ้นและฉันสามารถจินตนาการแนวคิดได้ชัดเจนยิ่งขึ้น - แนวคิดที่ฉันกำหนดให้ x เป็นค่าก่อนหน้าของ x (ไม่ว่าจะเป็นอะไร) แล้วเพิ่ม 5 ฉันคิดว่าสมองที่แตกต่างกันทำงานในวิธีที่ต่างกัน
Chris Harrison

48

อย่างน้อยก็ในหลาม , x += yและx = x + yสามารถทำสิ่งที่แตกต่างกันอย่างสิ้นเชิง

ตัวอย่างเช่นถ้าเราทำ

a = []
b = a

จากนั้นa += [3]จะส่งผลให้ในa == b == [3]ขณะที่a = a + [3]จะมีผลในและa == [3] b == []นั่นคือการ+=ปรับเปลี่ยนวัตถุในสถานที่ (ดีมันอาจจะทำคุณสามารถกำหนด__iadd__วิธีการทำอะไรที่คุณชอบมาก) ในขณะที่การ=สร้างวัตถุใหม่และผูกตัวแปรกับมัน

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


4
+1 สำหรับ__iadd__: มีภาษาที่คุณสามารถสร้างการอ้างอิงที่ไม่เปลี่ยนรูปไป datastructure ไม่แน่นอนที่operator +=มีการกำหนดเช่นสกาล่า: VSval sb = StringBuffer(); lb += "mutable structure" var s = ""; s += "mutable variable"ยิ่งปรับเปลี่ยนเนื้อหาของโครงสร้างข้อมูลในขณะที่หลังทำให้ตัวแปรชี้ไปที่ใหม่
แกะบิน

43

มันถูกเรียกว่าสำนวน การเขียนโปรแกรมสำนวนมีประโยชน์เพราะเป็นวิธีที่สอดคล้องกันในการเขียนโครงสร้างการเขียนโปรแกรมเฉพาะ

เมื่อใดก็ตามที่มีคนเขียนx += yคุณรู้ว่าxกำลังเพิ่มขึ้นyและไม่ใช่การดำเนินการที่ซับซ้อนมากขึ้น (เป็นการปฏิบัติที่ดีที่สุดโดยทั่วไปแล้วฉันจะไม่ผสมผสานการดำเนินการที่ซับซ้อนมากขึ้นและการจดชวเลขไวยากรณ์เหล่านี้) วิธีนี้เหมาะสมที่สุดเมื่อเพิ่มขึ้นทีละ 1


13
x ++ และ ++ x แตกต่างกันเล็กน้อยกับ x + = 1 และต่อกัน
Gary Willoughby

1
@Gary: ++xและx+=1เทียบเท่าใน C และ Java (อาจเป็น C #) แต่ไม่จำเป็นต้องเป็นเช่นนั้นใน C ++ เนื่องจากความหมายของโอเปอเรเตอร์ที่ซับซ้อนมี กุญแจสำคัญคือพวกเขาทั้งสองประเมินxครั้งเดียวเพิ่มตัวแปรทีละคนและมีผลลัพธ์ที่เป็นเนื้อหาของตัวแปรหลังจากการประเมินผล
Donal Fellows

3
เฟล @Donal: ความสำคัญแตกต่างกันดังนั้นไม่ได้เช่นเดียวกับ++x + 3 x += 1 + 3วงเล็บx += 1และมันเหมือนกัน ในฐานะที่เป็นงบด้วยตัวเองพวกเขาเหมือนกัน
David Thornley

1
@ DavidThornley: มันยากที่จะพูดว่า "พวกเขาเหมือนกัน" เมื่อพวกเขาทั้งสองมีพฤติกรรมที่ไม่ได้กำหนด :)
Lightness Races ใน Orbit

1
ไม่มีการแสดงออก++x + 3, x += 1 + 3หรือ(x += 1) + 3มีพฤติกรรมไม่ได้กำหนด (สมมติว่าค่าส่งผลให้ "พอดี")
John Hascall

42

หากต้องการให้จุดของ @ Pubby ชัดเจนขึ้นให้พิจารณา someObj.foo.bar.func(x, y, z).baz += 5

ไม่มีตัว+=ดำเนินการมีสองวิธีที่จะไป:

  1. someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5. นี่ไม่เพียง แต่ซ้ำซ้อนอย่างมากและมันยังช้าลงอีกด้วย ดังนั้นหนึ่งจะต้อง
  2. tmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5ใช้ตัวแปรชั่วคราว: มันก็โอเค แต่มันมีเสียงดังมากสำหรับสิ่งที่ง่าย นี่เป็นสิ่งที่ใกล้เคียงกับสิ่งที่เกิดขึ้นจริงในขณะทำงาน แต่น่าเบื่อที่จะเขียนและใช้เพียงแค่+=จะเปลี่ยนงานเป็นคอมไพเลอร์ / ล่าม

ข้อดี+=และตัวดำเนินการอื่น ๆ นั้นไม่อาจปฏิเสธได้ในขณะที่การชินกับมันเป็นเพียงเรื่องของเวลา


เมื่อคุณลึกเข้าไปในห่วงโซ่วัตถุ 3 ระดับคุณสามารถหยุดการดูแลเกี่ยวกับการเพิ่มประสิทธิภาพขนาดเล็กเช่น 1 และรหัสที่มีเสียงดังเช่น 2 แทนให้คิดทบทวนการออกแบบของคุณอีกครั้ง
Dorus

4
@Dorus: การแสดงออกที่ฉันเลือกเป็นเพียงตัวแทนโดยพลการสำหรับ "การแสดงออกที่ซับซ้อน" รู้สึกอิสระที่จะแทนที่ด้วยบางสิ่งบางอย่างในหัวของคุณที่คุณจะไม่ nitpick เกี่ยวกับ;)
back2dos

9
+1: นี่คือเหตุผลหลักสำหรับการเพิ่มประสิทธิภาพนี้ - ถูกต้องเสมอไม่ว่าการแสดงออกทางซ้ายมือจะซับซ้อนแค่ไหน
S.Lott

9
การพิมพ์ผิดในนั้นมีภาพประกอบที่ดีของสิ่งที่สามารถเกิดขึ้นได้
David Thornley

21

มันเป็นความจริงที่สั้นและง่ายขึ้นและเป็นความจริงที่อาจได้รับแรงบันดาลใจจากภาษาแอสเซมบลีพื้นฐาน แต่เหตุผลที่เป็นแนวปฏิบัติที่ดีที่สุดคือมันป้องกันข้อผิดพลาดทั้งชั้นและทำให้ง่ายต่อการตรวจสอบรหัสและตรวจสอบให้แน่ใจ มันทำอะไร

กับ

RidiculouslyComplexName += 1;

เนื่องจากมีชื่อตัวแปรที่เกี่ยวข้องเพียงชื่อเดียวคุณจึงมั่นใจว่าข้อความดังกล่าวเป็นอย่างไร

กับ RidiculouslyComplexName = RidiculosulyComplexName + 1;

มีข้อสงสัยเสมอว่าทั้งสองฝ่ายเหมือนกันทุกประการ คุณเห็นข้อบกพร่องหรือไม่ มันยิ่งเลวร้ายลงเมื่อมีการห้อยและตัวระบุ


5
มันแย่ลงมากในภาษาที่คำสั่งการมอบหมายสามารถสร้างตัวแปรโดยนัยโดยเฉพาะอย่างยิ่งถ้าภาษาเป็นกรณี ๆ ไป
supercat

14

ในขณะที่เครื่องหมาย + = เป็นสำนวนและสั้นกว่าสิ่งเหล่านี้ไม่ใช่เหตุผลที่ทำให้อ่านง่ายขึ้น ส่วนที่สำคัญที่สุดของการอ่านรหัสคือการจับคู่ไวยากรณ์กับความหมายและยิ่งใกล้กับไวยากรณ์ตรงกับกระบวนการคิดของโปรแกรมเมอร์มากเท่าใดก็ยิ่งอ่านได้ง่ายขึ้น (นี่เป็นเหตุผลว่าทำไมรหัสสำเร็จรูปแย่มาก: มันไม่ได้เป็นส่วนหนึ่งของความคิด กระบวนการ แต่ยังจำเป็นต้องทำให้ฟังก์ชั่นรหัส) ในกรณีนี้ความคิดคือ "การเพิ่มตัวแปร x 5" ไม่ใช่ "ให้ x เป็นค่าของ x บวก 5"

มีอีกหลายกรณีที่สัญกรณ์สั้นกว่านั้นไม่ดีสำหรับความสามารถในการอ่านได้เช่นเมื่อคุณใช้โอเปอเรเตอร์ ternary ซึ่งifคำสั่งจะเหมาะสมกว่า


11

สำหรับข้อมูลเชิงลึกเกี่ยวกับสาเหตุที่ผู้ประกอบการเหล่านี้อยู่ในภาษา 'สไตล์ C' เพื่อเริ่มต้นมีข้อความที่ตัดตอนมาจากK&R 1st Edition (1978), 34 ปีที่ผ่านมา:

นอกเหนือจากความรัดกุมผู้ประกอบการที่ได้รับมอบหมายมีข้อได้เปรียบที่สอดคล้องกับวิธีที่ผู้คนคิดดีกว่า เราพูดว่า "เพิ่ม 2 กับฉัน" หรือ "เพิ่มฉันด้วย 2," ไม่ใช่ "ใช้ i, เพิ่ม 2 จากนั้นใส่ผลลัพธ์กลับมาใน i" i += 2ดังนั้น นอกจากนี้สำหรับการแสดงออกที่ซับซ้อนเช่น

yyval[yypv[p3+p4] + yypv[p1+p2]] += 2

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

ฉันคิดว่ามันชัดเจนจากข้อความนี้ที่Brian KernighanและDennis Ritchie (K&R) เชื่อว่าผู้ประกอบการที่ได้รับมอบหมายช่วยให้อ่านรหัสได้ง่าย

เป็นเวลานานแล้วที่ K&R เขียนสิ่งนั้นและ 'แนวปฏิบัติที่ดีที่สุด' มากมายเกี่ยวกับวิธีที่ผู้คนควรเขียนรหัสมีการเปลี่ยนแปลงหรือพัฒนาขึ้นตั้งแต่นั้นมา แต่คำถามโปรแกรมเมอร์นี้การแลกเปลี่ยนข้อมูลเป็นครั้งแรกที่ฉันจำได้ว่ามีคนร้องเรียนเกี่ยวกับความสามารถในการอ่านแบบผสมดังนั้นฉันจึงสงสัยว่าโปรแกรมเมอร์หลายคนคิดว่าพวกเขาเป็นปัญหาหรือไม่? จากนั้นอีกครั้งในขณะที่ฉันพิมพ์คำถามนี้มี upvotes 95 ดังนั้นผู้คนอาจพบว่าพวกเขาสั่นสะเทือนเมื่ออ่านรหัส


9

นอกเหนือจากความสามารถในการอ่านแล้วพวกเขาทำสิ่งต่าง ๆ : +=ไม่ต้องประเมินตัวถูกดำเนินการด้านซ้ายสองครั้ง

ตัวอย่างเช่นexpr = expr + 5จะประเมินexprสองครั้ง (สมมติว่าexprไม่บริสุทธิ์)


สำหรับทุกคนยกเว้นคอมไพเลอร์ที่แปลกประหลาดมันไม่สำคัญ คอมไพเลอร์ส่วนใหญ่ฉลาดพอที่จะสร้างไบนารี่ที่เหมือนกันสำหรับexpr = expr + 5และexpr += 5
vsz

7
@vsz ไม่ได้ถ้าexprมีผลข้างเคียง
Pubby

6
@vsz: ใน C หากexprมีผลข้างเคียงแล้วexpr = expr + 5 ต้องเรียกใช้ผลข้างเคียงเหล่านั้นสองครั้ง
Keith Thompson

@Pubby: หากมีผลข้างเคียงก็ไม่มีปัญหาเกี่ยวกับ "ความสะดวกสบาย" และ "ความสามารถในการอ่าน" ซึ่งเป็นประเด็นของคำถามเดิม
vsz

2
ควรระมัดระวังเกี่ยวกับคำสั่ง "ผลข้างเคียง" เหล่านั้น การอ่านและเขียนvolatileเป็นผลข้างเคียงx=x+5และx+=5มีผลข้างเคียงที่เหมือนกันเมื่อxเป็นvolatile
MSalters

6

นอกจากข้อดีที่เห็นได้ชัดซึ่งคนอื่นอธิบายได้ดีเมื่อคุณมีชื่อที่ยาวมากมันมีขนาดกะทัดรัดกว่า

  MyVeryVeryVeryVeryVeryLongName += 1;

หรือ

  MyVeryVeryVeryVeryVeryLongName =  MyVeryVeryVeryVeryVeryLongName + 1;

6

มันกระชับ

มันสั้นกว่ามากในการพิมพ์ มันเกี่ยวข้องกับผู้ประกอบการน้อยลง มันมีพื้นที่ผิวน้อยลงและมีโอกาสสับสนน้อยลง

มันใช้ตัวดำเนินการเฉพาะเจาะจงมากขึ้น

นี่เป็นตัวอย่างที่วางแผนไว้และฉันไม่แน่ใจว่าคอมไพเลอร์ตัวจริงจะใช้สิ่งนี้หรือไม่ x + = y ใช้อาร์กิวเมนต์หนึ่งตัวและหนึ่งโอเปอเรเตอร์แล้วแก้ไข x แทน x = x + y สามารถเป็นตัวแทนระดับกลางของ x = z โดยที่ z คือ x + y หลังใช้ตัวดำเนินการสองตัวการเพิ่มและการกำหนดและตัวแปรชั่วคราว โอเปอเรเตอร์เดียวทำให้ชัดเจนที่สุดว่าด้านค่าไม่สามารถเป็นอย่างอื่นได้นอกจาก y และไม่จำเป็นต้องตีความ ในทางทฤษฎีอาจมีซีพียูแฟนซีบางตัวที่มีตัวดำเนินการบวกเท่ากับที่ทำงานเร็วกว่าตัวดำเนินการบวกและตัวดำเนินการกำหนดค่าในซีรีส์


2
ในทางทฤษฎี schmeoretically ADDคำแนะนำเกี่ยวกับซีพียูมีหลายสายพันธุ์ที่ทำงานโดยตรงบนลงทะเบียนหรือหน่วยความจำที่ใช้ลงทะเบียนอื่น ๆ หน่วยความจำหรือคงเป็น addend ที่สอง ไม่สามารถใช้ชุดค่าผสมทั้งหมดได้ (เช่นเพิ่มหน่วยความจำในหน่วยความจำ) แต่มีประโยชน์เพียงพอ ในอัตราใด ๆ คอมไพเลอร์ใด ๆ กับการเพิ่มประสิทธิภาพที่ดีจะได้รู้ว่าเพื่อสร้างรหัสเดียวกันสำหรับในขณะที่มันทำกับx = x + y x += y
Blrfl

ดังนั้น "ประดิษฐ์"
Mark Canlas

5

มันเป็นสำนวนที่ดี ไม่ว่าจะเร็วหรือไม่ก็ขึ้นอยู่กับภาษา ใน C มันเร็วกว่าเพราะแปลเป็นคำสั่งเพื่อเพิ่มตัวแปรทางด้านขวามือ ภาษาสมัยใหม่รวมถึง Python, Ruby, C, C ++ และ Java ทั้งหมดสนับสนุน op = syntax มันกะทัดรัดและคุณจะชินกับมันได้อย่างรวดเร็ว เนื่องจากคุณจะเห็นมันมากมายในรหัสของคนอื่น (OPC) คุณอาจคุ้นเคยกับมันและใช้มัน นี่คือสิ่งที่เกิดขึ้นในสองภาษาอื่น ๆ

ใน Python การพิมพ์x += 5ยังคงทำให้เกิดการสร้างวัตถุจำนวนเต็ม 1 (แม้ว่ามันอาจจะดึงมาจากกลุ่ม) และสถานเลี้ยงเด็กกำพร้าของวัตถุจำนวนเต็มที่มี 5

ใน Java มันทำให้เกิดการส่งข้อความโดยปริยาย ลองพิมพ์

int x = 4;
x = x + 5.2  // This causes a compiler error
x += 5.2     // This is not an error; an implicit cast is done.

ภาษาสมัยใหม่ที่จำเป็น ใน (บริสุทธิ์) ภาษาทำงานx+=5มีความหมายเพียงเล็กน้อยเช่นx=x+5; เช่นใน Haskell หลังไม่ทำให้xเพิ่มขึ้น 5 - มันเกิดขึ้นซ้ำวงวนซ้ำ ใครต้องการชวเลขสำหรับสิ่งนั้น
leftaroundabout

ไม่จำเป็นว่าจะต้องเร็วขึ้นด้วยโปรแกรมคอมไพเลอร์สมัยใหม่: แม้แต่ใน C
HörmannHH

ความเร็วของภาษา+=นั้นไม่ขึ้นอยู่กับว่ามันขึ้นอยู่กับโปรเซสเซอร์ ยกตัวอย่างเช่น X86 เป็นสถาปัตยกรรมที่อยู่สองแห่งรองรับเพียง+=ภาษาดั้งเดิม คำแถลงอย่างนี้a = b + c;จะต้องรวบรวมa = b; a += c;เพราะไม่มีคำสั่งใดที่สามารถนำผลลัพธ์ของการเติมใด ๆ ไปที่อื่นนอกเหนือจากที่อยู่ในสถานที่แห่งหนึ่งของการสรุป +=สถาปัตยกรรมพลังงานโดยคมชัดเป็นสถาปัตยกรรมสามอยู่ที่ไม่ได้มีคำสั่งพิเศษสำหรับ ในสถาปัตยกรรมนี้คำสั่งa += b;และa = a + b;รวบรวมเป็นรหัสเดียวกันเสมอ
cmaster

4

ผู้ประกอบการเช่น+=มีประโยชน์มากเมื่อคุณใช้ตัวแปรเป็นตัวสะสมนั่นคือผลรวมสะสม :

x += 2;
x += 5;
x -= 3;

อ่านง่ายกว่ามาก:

x = x + 2;
x = x + 5;
x = x - 3;

ในกรณีแรก, แนวคิด, xคุณปรับเปลี่ยนค่าใน ในกรณีที่สองคุณกำลังคำนวณค่าใหม่และกำหนดให้กับxแต่ละครั้ง และในขณะที่คุณอาจไม่เคยเขียนโค้ดที่ค่อนข้างง่ายความคิดยังคงเหมือนเดิม ... โฟกัสอยู่ที่สิ่งที่คุณกำลังทำกับค่าที่มีอยู่แทนที่จะสร้างค่าใหม่บางอย่าง


สำหรับฉันมันเป็นสิ่งที่ตรงกันข้ามสิ่งแรกคือสิ่งสกปรกบนหน้าจอและที่สองสะอาดและอ่านง่าย
มิคาอิล V

1
จังหวะที่แตกต่างกันสำหรับกลุ่มคนที่แตกต่างกัน @MikhailV แต่ถ้าคุณยังใหม่กับการเขียนโปรแกรมคุณอาจเปลี่ยนมุมมองของคุณหลังจากนั้นไม่นาน
Caleb

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

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

1

พิจารณาสิ่งนี้

(some_object[index])->some_other_object[more] += 5

D0 คุณต้องการเขียนจริงๆ

(some_object[index])->some_other_object[more] = (some_object[index])->some_other_object[more] + 5

1

คำตอบอื่น ๆ กำหนดเป้าหมายกรณีที่พบบ่อยมากขึ้น แต่มีเหตุผลอื่น: ในบางภาษาการเขียนโปรแกรมก็สามารถมากเกินไป; เช่นสกาล่า


บทเรียนสกาล่าขนาดเล็ก:

var j = 5 #Creates a variable
j += 4    #Compiles

val i = 5 #Creates a constant
i += 4    #Doesn’t compile

ถ้าชั้นเพียงกำหนด+ผู้ประกอบการย่อมเป็นทางลัดของx+=yx=x+y

อย่างไรก็ตามหากคลาสโอเวอร์โหลด+=พวกเขาจะไม่:

var a = ""
a += "This works. a now points to a new String."

val b = ""
b += "This doesn’t compile, as b cannot be reassigned."

val c = StringBuffer() #implements +=
c += "This works, as StringBuffer implements “+=(c: String)”."

นอกจากนี้ตัวดำเนินการ+และ+=เป็นตัวดำเนินการที่แยกกันสองตัว (และไม่เพียง แต่เหล่านี้: + a, ++ a, a ++, a + ba + = b เป็นตัวดำเนินการที่แตกต่างกันเช่นกัน); ในภาษาที่มีผู้ให้บริการโหลดมากเกินไปสิ่งนี้อาจสร้างสถานการณ์ที่น่าสนใจ ตามที่อธิบายไว้ข้างต้น - หากคุณใช้งานเกินขนาด+เพื่อดำเนินการเพิ่มโปรดทราบว่า+=จะต้องมีการใช้งานมากเกินไปเช่นกัน


"ถ้าคลาสกำหนดตัวดำเนินการ + เท่านั้น, x + = y เป็นทางลัดของ x = x + y" แต่แน่นอนมันยังทำงานในทางอื่น ๆ ? ใน C ++ มันเป็นเรื่องธรรมดามากที่จะกำหนดเป็นอันดับแรก+=จากนั้นกำหนดa+bเป็น "ทำสำเนาaวางbไว้โดยใช้+=แล้วส่งคืนสำเนาของa"
leftaroundabout

1

พูดมันครั้งเดียวครั้งเดียว: x = x + 1ฉันพูด 'x' สองครั้ง

แต่ไม่เคยเขียน 'a = b + = 1' หรือเราจะต้องฆ่าลูกแมว 10 ตัวหนู 27 ตัวสุนัขและหนูแฮมสเตอร์


คุณไม่ควรเปลี่ยนค่าของตัวแปรเพราะจะทำให้ง่ายต่อการพิสูจน์ว่ารหัสนั้นถูกต้อง - ดูการโปรแกรมการทำงาน อย่างไรก็ตามถ้าคุณทำมันจะดีกว่าไม่พูดอะไรเพียงครั้งเดียว


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