Tại sao các phím tắt như x + = y được coi là thực hành tốt?


305

Tôi không biết những thứ này thực sự được gọi là gì, nhưng tôi thấy chúng mọi lúc. Việc triển khai Python là một cái gì đó như:

x += 5như một ký hiệu viết tắt cho x = x + 5.

Nhưng tại sao điều này được coi là thực hành tốt? Tôi đã chạy qua nó trong gần như mọi cuốn sách hoặc hướng dẫn lập trình mà tôi đã đọc cho Python, C, R , v.v. Tôi hiểu rằng nó thuận tiện, tiết kiệm ba tổ hợp phím bao gồm cả không gian. Nhưng dường như họ luôn làm tôi khó chịu khi tôi đọc mã, và ít nhất là trong tâm trí của tôi, làm cho nó ít đọc hơn , không nhiều hơn.

Tôi có thiếu một số lý do rõ ràng và rõ ràng những thứ này được sử dụng ở khắp mọi nơi không?


@EricLippert: C # có xử lý việc này giống như câu trả lời hàng đầu được mô tả không? Có thực sự nói CLR khôn ngoan x += 5hơn x = x + 5không? Hoặc nó thực sự chỉ là cú pháp đường như bạn đề nghị?
thịt

24
@bledh: Các chi tiết nhỏ về cách người ta thể hiện một bổ sung trong mã nguồn có ảnh hưởng đến hiệu quả của mã thực thi kết quả có thể đã xảy ra vào năm 1970; Nó chắc chắn không phải bây giờ. Tối ưu hóa trình biên dịch là tốt, và bạn có những lo lắng lớn hơn một nano giây ở đây hoặc ở đó. Ý tưởng rằng toán tử + = được phát triển "hai mươi năm trước" rõ ràng là sai; Dennis Richie quá cố đã phát triển C từ năm 1969 đến năm 1973 tại Bell Labs.
Eric Lippert


5
Hầu hết các lập trình viên chức năng sẽ xem xét thực hành xấu này.
Pete Kirkham

Câu trả lời:


598

Nó không phải tốc ký.

Các +=biểu tượng xuất hiện trong ngôn ngữ C vào những năm 1970, và - với ý tưởng C của "lắp ráp thông minh" tương ứng với một lệnh máy và adressing chế độ rõ ràng khác nhau:

Những thứ như " i=i+1", "i+=1"và" ++i", mặc dù ở mức trừu tượng tạo ra hiệu ứng tương tự, tương ứng ở mức thấp với cách làm việc khác của bộ xử lý.

Cụ thể là ba biểu thức đó, giả sử ibiến nằm trong địa chỉ bộ nhớ được lưu trong thanh ghi CPU (hãy đặt tên cho nó D- nghĩ về nó như một "con trỏ tới int") và ALU của bộ xử lý lấy tham số và trả về kết quả trong một "Bộ tích lũy" (hãy gọi nó là A - nghĩ về nó như một số nguyên).

Với những hạn chế này (rất phổ biến trong tất cả các bộ vi xử lý từ thời kỳ đó), bản dịch rất có thể sẽ là

;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

Cách đầu tiên để thực hiện nó là không tối ưu, nhưng nó tổng quát hơn khi hoạt động với các biến thay vì hằng ( ADD A, Bhoặc ADD A, (D+x)) hoặc khi dịch các biểu thức phức tạp hơn (tất cả đều sôi lên trong hoạt động đẩy mức độ ưu tiên thấp trong ngăn xếp, gọi mức độ ưu tiên cao, pop và lặp lại cho đến khi tất cả các đối số đã được loại bỏ).

Thứ hai là điển hình hơn của "máy trạng thái": chúng tôi không còn "đánh giá biểu thức", mà là "vận hành một giá trị": chúng tôi vẫn sử dụng ALU, nhưng tránh di chuyển các giá trị xung quanh là kết quả được phép thay thế tham số. Các loại hướng dẫn này không thể được sử dụng khi cần có biểu thức phức tạp hơn: i = 3*i + i-2không thể được vận hành tại chỗ, vì iđược yêu cầu nhiều lần hơn.

Đơn giản thứ ba - thậm chí không xem xét ý tưởng "bổ sung", mà sử dụng một mạch "nguyên thủy" hơn (theo nghĩa tính toán) cho một bộ đếm. Lệnh được rút ngắn, tải nhanh hơn và thực thi ngay lập tức, do mạng kết hợp cần phải trang bị thêm một thanh ghi để làm cho nó trở thành một bộ đếm nhỏ hơn và do đó nhanh hơn so với một bộ cộng đầy đủ.

Với các trình biên dịch hiện đại (tham khảo C, bây giờ), cho phép tối ưu hóa trình biên dịch, sự tương ứng có thể được hoán đổi dựa trên sự thuận tiện, nhưng vẫn có một sự khác biệt về khái niệm trong ngữ nghĩa.

x += 5 có nghĩa

  • Tìm địa điểm được xác định bởi x
  • Thêm 5 vào nó

Nhưng x = x + 5có nghĩa là:

  • Đánh giá x + 5
    • Tìm địa điểm được xác định bởi x
    • Sao chép x vào một bộ tích lũy
    • Thêm 5 vào bộ tích lũy
  • Lưu trữ kết quả trong x
    • Tìm địa điểm được xác định bởi x
    • Sao chép bộ tích lũy vào nó

Tất nhiên, tối ưu hóa có thể

  • nếu "tìm x" không có tác dụng phụ, hai "tìm" có thể được thực hiện một lần (và x trở thành địa chỉ được lưu trong thanh ghi con trỏ)
  • hai bản sao có thể được tách ra nếu THÊM được áp dụng &xthay thế cho bộ tích lũy

do đó làm cho mã được tối ưu hóa trùng với mã x += 5.

Nhưng điều này chỉ có thể được thực hiện nếu "tìm x" không có tác dụng phụ, nếu không

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

*(x()) += 5;

là khác nhau về mặt ngữ nghĩa, vì các x()tác dụng phụ (thừa nhận x()là một chức năng làm những điều kỳ lạ xung quanh và trả lại một int*) sẽ được sản xuất hai lần hoặc một lần.

Sự tương đương giữa x = x + yx += ydo đó là do trường hợp cụ thể trong đó +==được áp dụng cho giá trị l trực tiếp.

Để chuyển sang Python, nó đã kế thừa cú pháp từ C, nhưng vì không có bản dịch / tối ưu hóa TRƯỚC KHI thực thi trong các ngôn ngữ được dịch, nên mọi thứ không nhất thiết phải liên quan mật thiết như vậy (vì có một bước ít phân tích cú pháp hơn). Tuy nhiên, một trình thông dịch có thể tham khảo các thói quen thực thi khác nhau cho ba loại biểu thức, lợi dụng mã máy khác nhau tùy thuộc vào cách biểu thức được hình thành và trên bối cảnh đánh giá.


Dành cho ai thích chi tiết hơn ...

Mỗi CPU đều có ALU (đơn vị logic số học), về bản chất, là một mạng tổ hợp có đầu vào và đầu ra được "cắm" vào các thanh ghi và / hoặc bộ nhớ tùy thuộc vào opcode của lệnh.

Các hoạt động nhị phân thường được triển khai như là "công cụ sửa đổi của thanh ghi tích lũy với đầu vào được lấy" ở đâu đó ", trong đó có thể ở đâu đó - bên trong dòng lệnh (điển hình cho contant kê khai: ADD A 5) - bên trong một sổ đăng ký khác (điển hình cho tính toán biểu thức với tạm thời: ví dụ ADD AB) - bên trong bộ nhớ, tại một địa chỉ được cung cấp bởi một thanh ghi (điển hình của việc tìm nạp dữ liệu, ví dụ: ADD A (H)) - H, trong trường hợp này, hoạt động như một con trỏ hội nghị.

Với mã giả này, x += 5

ADD (X) 5

trong khi x = x+5

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

Nghĩa là, x + 5 cung cấp tạm thời được gán sau. x += 5hoạt động trực tiếp trên x.

Việc triển khai thực tế phụ thuộc vào tập lệnh thực của bộ xử lý: Nếu không có ADD (.) copcode, mã đầu tiên sẽ trở thành mã thứ hai: không có cách nào.

Nếu có một opcode như vậy và tối ưu hóa được kích hoạt, biểu thức thứ hai, sau khi loại bỏ các bước di chuyển ngược và điều chỉnh opcode của thanh ghi, trở thành biểu thức đầu tiên.


90
+1 cho câu trả lời duy nhất giải thích rằng nó được sử dụng để ánh xạ tới mã máy khác (và hiệu quả hơn) vào thời xa xưa.
Péter Török

11
@KeithThndry Điều đó đúng nhưng người ta không thể phủ nhận rằng hội thảo có ảnh hưởng rất lớn đến việc thiết kế ngôn ngữ C (và sau đó là tất cả các ngôn ngữ kiểu C)
MattDavey

51
Erm, "+ =" không ánh xạ tới "inc" (nó ánh xạ tới "thêm"), "++" ánh xạ thành "inc".
Brendan

11
Bởi "20 năm trước", tôi nghĩ bạn có nghĩa là "30 năm trước". Và BTW, COBOL đã đánh bại C thêm 20 năm nữa với: THÊM 5 ĐẾN X.
JoelFan

10
Tuyệt vời về lý thuyết; sai trong thực tế. X86 ASM INC chỉ thêm 1, vì vậy nó không ảnh hưởng đến toán tử "thêm và gán" được thảo luận ở đây (đây sẽ là một câu trả lời tuyệt vời cho "++" và "-" mặc dù).
Mark Brackett

281

Tùy thuộc vào cách bạn nghĩ về nó, nó thực sự dễ hiểu hơn bởi vì nó đơn giản hơn. Lấy ví dụ:

x = x + 5 gọi xử lý tinh thần của "lấy x, thêm năm cho nó, và sau đó gán giá trị mới đó trở lại x"

x += 5 có thể được coi là "tăng x 5"

Vì vậy, nó không chỉ là tốc ký, nó thực sự mô tả các chức năng trực tiếp hơn nhiều. Khi đọc qua các mã nguồn, việc nắm bắt sẽ dễ dàng hơn nhiều.


32
+1, tôi hoàn toàn đồng ý. Tôi tham gia lập trình khi còn bé, khi tâm trí tôi dễ bị uốn nắn, và x = x + 5vẫn còn làm tôi lo lắng. Khi tôi học toán ở độ tuổi muộn hơn, nó càng làm phiền tôi nhiều hơn. Sử dụng x += 5là mô tả đáng kể hơn và có ý nghĩa hơn như là một biểu thức.
Đa thức

44
Cũng có trường hợp biến có tên dài: reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1... oh noes !!! một lỗi đánh máy

9
@Matt Fenwick: Nó không phải là một tên biến dài; nó có thể là một biểu hiện của một số loại. Những điều đó có thể còn khó xác minh hơn nữa và người đọc phải dành nhiều sự chú ý để đảm bảo chúng giống nhau.
David Thornley

32
X = X + 5 là một loại hack khi mục tiêu của bạn là tăng x lên 5.
JeffO

1
@Polynomial Tôi nghĩ rằng tôi đã bắt gặp khái niệm x = x + 5 khi còn bé. 9 tuổi nếu tôi nhớ chính xác - và nó có ý nghĩa hoàn hảo với tôi và nó vẫn có ý nghĩa hoàn hảo với tôi bây giờ và rất thích nó với x + = 5. Đầu tiên là dài dòng hơn nhiều và tôi có thể tưởng tượng khái niệm rõ ràng hơn nhiều - ý tưởng rằng tôi gán x là giá trị trước đó của x (bất kể đó là gì) và sau đó thêm 5. Tôi đoán các bộ não khác nhau hoạt động theo những cách khác nhau.
Chris Harrison

48

Ít nhất là trong Python , x += yx = x + ycó thể làm những việc hoàn toàn khác nhau.

Ví dụ, nếu chúng ta làm

a = []
b = a

sau đó a += [3]sẽ dẫn đến a == b == [3], trong khi a = a + [3]sẽ dẫn đến a == [3]b == []. Đó là, +=sửa đổi đối tượng tại chỗ (tốt, nó có thể làm, bạn có thể xác định __iadd__phương thức để thực hiện khá nhiều thứ bạn thích), đồng thời =tạo một đối tượng mới và liên kết biến với nó.

Điều này rất quan trọng khi thực hiện công việc số với NumPy , vì bạn thường kết thúc với nhiều tham chiếu đến các phần khác nhau của một mảng và điều quan trọng là đảm bảo rằng bạn không vô tình sửa đổi một phần của mảng có các tham chiếu khác, hoặc không cần sao chép mảng (có thể rất tốn kém).


4
1 cho __iadd__: có ngôn ngữ mà bạn có thể tạo một tài liệu tham khảo không thay đổi đến một datastructure có thể thay đổi, nơi operator +=được xác định, ví dụ như scala: val sb = StringBuffer(); lb += "mutable structure"vs var s = ""; s += "mutable variable". tiếp tục sửa đổi nội dung của cơ sở hạ tầng trong khi cái sau làm cho điểm biến thành một cái mới.
cừu bay

43

Nó được gọi là một thành ngữ . Thành ngữ lập trình rất hữu ích vì chúng là một cách nhất quán để viết một cấu trúc lập trình cụ thể.

Bất cứ khi nào ai đó viết x += ybạn biết rằng nó xđang được tăng lên yvà không phải là một hoạt động phức tạp hơn (như một cách thực hành tốt nhất, thông thường tôi sẽ không trộn lẫn các hoạt động phức tạp hơn và các tốc ký cú pháp này). Điều này có ý nghĩa nhất khi tăng thêm 1.


13
x ++ và ++ x hơi khác so với x + = 1 và với nhau.
Gary Willoughby

1
@Gary: ++xx+=1tương đương trong C và Java (cũng có thể là C #) mặc dù không nhất thiết phải như vậy trong C ++ vì ngữ nghĩa toán tử phức tạp ở đó. Điều quan trọng là cả hai đều đánh giá xmột lần, tăng biến số lên một và có kết quả là nội dung của biến sau khi đánh giá.
Donal Fellows

3
@Donal Fellows: Ưu tiên là khác nhau, vì vậy ++x + 3không giống như x += 1 + 3. Dấu ngoặc đơn x += 1và nó giống hệt nhau. Như tuyên bố của chính họ, chúng giống hệt nhau.
David Thornley

1
@DavidThornley: Thật khó để nói "chúng giống hệt nhau" khi cả hai có hành vi không xác định :)
Các cuộc đua Lightness trong quỹ đạo

1
Không có biểu thức nào ++x + 3, x += 1 + 3hoặc (x += 1) + 3có hành vi không xác định (giả sử giá trị kết quả là "phù hợp").
John Hascall

42

Để nêu quan điểm của @ Pubby rõ ràng hơn một chút, hãy xem xét someObj.foo.bar.func(x, y, z).baz += 5

Không có +=toán tử, có hai cách để đi:

  1. someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5. Điều này không chỉ dư thừa và lâu dài, mà còn chậm hơn. Vì vậy, người ta sẽ phải
  2. Sử dụng một biến tạm thời : tmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5. Điều này là ổn, nhưng nó rất nhiều tiếng ồn cho một điều đơn giản. Điều này thực sự rất gần với những gì xảy ra trong thời gian chạy, nhưng thật tẻ nhạt khi viết và chỉ cần sử dụng +=sẽ chuyển công việc sang trình biên dịch / trình thông dịch.

Lợi thế của +=và các nhà khai thác khác như vậy là không thể phủ nhận, trong khi làm quen với chúng chỉ là vấn đề thời gian.


Khi bạn đã sâu 3 cấp vào chuỗi đối tượng, bạn có thể ngừng quan tâm đến các tối ưu hóa nhỏ như 1 và mã ồn ào như 2. Thay vào đó, hãy suy nghĩ về thiết kế của bạn một lần nữa.
Dorus

4
@ Điệp khúc: Biểu thức tôi chọn chỉ là một đại diện tùy ý cho "biểu thức phức tạp". Hãy thoải mái thay thế nó bằng một cái gì đó trong đầu bạn, mà bạn sẽ không hiểu về nó;)
back2dos

9
+1: Đây là lý do chính cho việc tối ưu hóa này - nó luôn luôn chính xác, bất kể biểu thức phía bên trái phức tạp đến mức nào.
S.Lott

9
Đặt một lỗi đánh máy trong đó là một minh họa tốt đẹp về những gì có thể xảy ra.
David Thornley

21

Đúng là nó ngắn hơn và dễ dàng hơn, và đúng là nó có thể được truyền cảm hứng từ ngôn ngữ lắp ráp cơ bản, nhưng lý do thực tiễn tốt nhất là nó ngăn ngừa cả một lớp lỗi và giúp việc xem lại mã dễ dàng hơn và chắc chắn Những gì nó làm.

Với

RidiculouslyComplexName += 1;

Vì chỉ có một tên biến liên quan, bạn chắc chắn câu lệnh đó làm gì.

Với RidiculouslyComplexName = RidiculosulyComplexName + 1;

Luôn có nghi ngờ rằng hai bên giống hệt nhau. Bạn đã thấy lỗi chưa? Nó thậm chí còn tồi tệ hơn khi đăng ký và vòng loại có mặt.


5
Nó trở nên tồi tệ hơn trong các ngôn ngữ nơi các câu lệnh gán có thể tạo ra các biến, đặc biệt nếu các ngôn ngữ có phân biệt chữ hoa chữ thường.
supercat

14

Mặc dù ký hiệu + = là thành ngữ và ngắn hơn, nhưng đây không phải là lý do tại sao nó dễ đọc hơn. Phần quan trọng nhất của mã đọc là cú pháp ánh xạ thành ý nghĩa và do đó cú pháp càng khớp với các quá trình suy nghĩ của lập trình viên, nó sẽ càng dễ đọc hơn (đây cũng là lý do tại sao mã soạn sẵn là xấu: nó không phải là một phần của suy nghĩ xử lý, nhưng vẫn cần thiết để thực hiện chức năng mã). Trong trường hợp này, ý nghĩ là "biến tăng x thêm 5", chứ không phải "đặt x là giá trị của x cộng 5".

Có những trường hợp khác mà một ký hiệu ngắn hơn có hại cho khả năng đọc, ví dụ như khi bạn sử dụng một toán tử ternary trong đó một ifcâu lệnh sẽ phù hợp hơn.


11

Để hiểu rõ hơn về lý do tại sao các nhà khai thác này sử dụng ngôn ngữ 'kiểu C' để bắt đầu, có đoạn trích này từ K & R 1st Edition (1978), 34 năm trước:

Ngoài sự đồng nhất, các toán tử gán có lợi thế là chúng tương ứng tốt hơn với cách mọi người nghĩ. Chúng tôi nói "thêm 2 vào i" hoặc "tăng i lên 2", chứ không phải "lấy i, thêm 2, sau đó đưa kết quả trở lại vào i". Như vậy i += 2. Ngoài ra, đối với một biểu thức phức tạp như

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

toán tử gán làm cho mã dễ hiểu hơn, vì người đọc không cần phải kiểm tra một cách tỉ mỉ rằng hai biểu thức dài thực sự giống nhau hoặc tự hỏi tại sao chúng không như vậy. Và một toán tử gán thậm chí có thể giúp trình biên dịch tạo mã hiệu quả hơn.

Tôi nghĩ rằng rõ ràng từ đoạn văn này rằng Brian KernighanDennis Ritchie (K & R), tin rằng các toán tử gán hợp chất đã giúp dễ đọc mã.

Đã lâu rồi kể từ khi K & R viết điều đó, và rất nhiều 'thực tiễn tốt nhất' về cách mọi người nên viết mã đã thay đổi hoặc phát triển kể từ đó. Nhưng câu hỏi này của lập trình viên.stackexchange là lần đầu tiên tôi có thể nhớ lại ai đó đang lên tiếng phàn nàn về khả năng đọc các bài tập ghép, vì vậy tôi tự hỏi liệu nhiều lập trình viên có thấy chúng là một vấn đề không? Sau đó, một lần nữa, khi tôi gõ câu hỏi này, có 95 câu hỏi, vì vậy có lẽ mọi người sẽ thấy chúng bị chói tai khi đọc mã.


9

Bên cạnh khả năng đọc, họ thực sự làm những việc khác nhau: +=không phải đánh giá toán hạng bên trái của nó hai lần.

Chẳng hạn, expr = expr + 5sẽ tiến hành exprhai lần (giả sử exprlà không trong sạch).


Đối với tất cả trừ các trình biên dịch kỳ lạ nhất, nó không thành vấn đề. Hầu hết các trình biên dịch đều đủ thông minh để tạo cùng một nhị phân cho expr = expr + 5expr += 5
vsz

7
@vsz Không nếu exprcó tác dụng phụ.
Pubby

6
@vsz: Trong C, nếu exprcó tác dụng phụ thì expr = expr + 5 phải gọi những tác dụng phụ đó hai lần.
Keith Thompson

@Pubby: nếu nó có tác dụng phụ, không có vấn đề gì về "sự tiện lợi" và "khả năng đọc", đó là điểm của câu hỏi ban đầu.
vsz

2
Tốt hơn nên cẩn thận về những tuyên bố "tác dụng phụ". Đọc và ghi volatilelà tác dụng phụ, và x=x+5x+=5có tác dụng phụ tương tự khi xvolatile
MSalters

6

Bên cạnh những giá trị rõ ràng mà người khác mô tả rất tốt, khi bạn có tên rất dài thì nó nhỏ gọn hơn.

  MyVeryVeryVeryVeryVeryLongName += 1;

hoặc là

  MyVeryVeryVeryVeryVeryLongName =  MyVeryVeryVeryVeryVeryLongName + 1;

6

Nó súc tích.

Nó ngắn hơn nhiều để gõ. Nó liên quan đến ít nhà khai thác hơn. Nó có diện tích bề mặt ít hơn và ít cơ hội cho sự nhầm lẫn.

Nó sử dụng một toán tử cụ thể hơn.

Đây là một ví dụ giả định và tôi không chắc liệu trình biên dịch thực tế có thực hiện việc này không. x + = y thực sự sử dụng một đối số và một toán tử và sửa đổi x tại chỗ. x = x + y có thể có biểu diễn trung gian là x = z trong đó z là x + y. Cái sau sử dụng hai toán tử, phép cộng và phép gán và một biến tạm thời. Toán tử đơn làm cho cực kỳ rõ ràng rằng phía giá trị không thể là gì khác ngoài y và không cần phải giải thích. Và về mặt lý thuyết có thể có một số CPU ưa thích có toán tử cộng bằng chạy nhanh hơn toán tử cộng và toán tử gán trong chuỗi.


2
Về mặt lý thuyết. Các ADDhướng dẫn trên nhiều CPU có các biến thể hoạt động trực tiếp trên các thanh ghi hoặc bộ nhớ bằng cách sử dụng các thanh ghi, bộ nhớ hoặc hằng số khác làm phụ lục thứ hai. Không phải tất cả các kết hợp đều có sẵn (ví dụ: thêm bộ nhớ vào bộ nhớ), nhưng có đủ để hữu ích. Ở bất kỳ giá nào, bất kỳ trình biên dịch nào có trình tối ưu hóa tốt sẽ biết để tạo cùng một mã cho x = x + yx += y.
Blrfl

Do đó "chiếm".
Đánh dấu Canlas

5

Đó là một thành ngữ tốt đẹp. Cho dù nó nhanh hơn hay không phụ thuộc vào ngôn ngữ. Trong C, nó nhanh hơn bởi vì nó chuyển thành một lệnh để tăng biến ở phía bên tay phải. Các ngôn ngữ hiện đại, bao gồm Python, Ruby, C, C ++ và Java đều hỗ trợ cú pháp op =. Nó nhỏ gọn và bạn nhanh chóng làm quen với nó. Vì bạn sẽ thấy nó rất nhiều trong mã của người khác (OPC), bạn cũng có thể quen với nó và sử dụng nó. Đây là những gì xảy ra trong một vài ngôn ngữ khác.

Trong Python, việc gõ x += 5vẫn gây ra việc tạo đối tượng số nguyên 1 (mặc dù nó có thể được rút ra từ một nhóm) và mồ côi của đối tượng số nguyên chứa 5.

Trong Java, nó gây ra một diễn viên ngầm xảy ra. Hãy thử gõ

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.

Ngôn ngữ mệnh lệnh hiện đại . Trong các ngôn ngữ chức năng (thuần túy) x+=5có rất ít ý nghĩa như x=x+5; ví dụ trong Haskell, cái sau không làm xtăng thêm 5 - thay vào đó, nó phát sinh một vòng lặp đệ quy vô hạn. Ai muốn viết tắt cho điều đó?
rẽ trái

Nó không nhất thiết phải nhanh hơn với các trình biên dịch hiện đại: ngay cả trong C.
HörmannHH

Tốc độ +=không phụ thuộc quá nhiều vào ngôn ngữ vì nó phụ thuộc vào bộ xử lý . Ví dụ, X86 là một kiến ​​trúc hai địa chỉ, nó chỉ hỗ trợ +=nguyên bản. Một câu lệnh như a = b + c;phải được biên dịch a = b; a += c;vì đơn giản là không có hướng dẫn nào có thể đặt kết quả của phép cộng ở bất kỳ nơi nào khác ngoài vị trí của một trong các phép triệu hồi. Kiến trúc Power, ngược lại, là kiến ​​trúc ba địa chỉ không có các lệnh đặc biệt cho +=. Trên kiến ​​trúc này, các câu lệnh a += b;a = a + b;luôn biên dịch theo cùng một mã.
cmaster

4

Các toán tử như +=rất hữu ích khi bạn đang sử dụng một biến như một bộ tích lũy , tức là tổng cộng đang chạy:

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

Dễ đọc hơn nhiều so với:

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

Trong trường hợp đầu tiên, về mặt khái niệm, bạn đang sửa đổi giá trị trong x. Trong trường hợp thứ hai, bạn đang tính toán một giá trị mới và gán nó cho xmỗi lần. Và mặc dù bạn có thể không bao giờ viết mã khá đơn giản, ý tưởng vẫn như cũ ... trọng tâm là những gì bạn đang làm với một giá trị hiện tại thay vì tạo ra một số giá trị mới.


Đối với tôi nó hoàn toàn ngược lại - biến thể đầu tiên giống như bụi bẩn trên màn hình và thứ hai là sạch sẽ và dễ đọc.
Mikhail V

1
Các nét khác nhau cho những người khác nhau, @MikhailV, nhưng nếu bạn mới lập trình, bạn có thể thay đổi quan điểm của mình sau một thời gian.
Caleb

Tôi không quá mới mẻ với lập trình, tôi chỉ nghĩ rằng bạn chỉ đơn giản là đã quen với ký hiệu + = trong một thời gian dài và do đó bạn có thể đọc nó. Điều này ban đầu không làm cho nó trở thành bất kỳ cú pháp dễ nhìn nào, do đó, một toán tử + được bao quanh bởi các khoảng trắng sẽ sạch hơn một cách khách quan sau đó + = và các bạn bè hầu như không thể phân biệt được. Không phải là câu trả lời của bạn là không chính xác, nhưng người ta không nên quá tin tưởng vào thói quen của chính mình khi đưa ra những suy đoán "dễ đọc" như thế nào.
Mikhail V

Cảm giác của tôi là về sự khác biệt về khái niệm giữa tích lũy và lưu trữ các giá trị mới hơn là làm cho biểu thức nhỏ gọn hơn. Hầu hết các ngôn ngữ lập trình mệnh lệnh đều có các toán tử tương tự nhau, vì vậy tôi dường như không phải là người duy nhất thấy nó hữu ích. Nhưng như tôi đã nói, đột quỵ khác nhau cho những người khác nhau. Đừng sử dụng nó nếu bạn không thích nó. Nếu bạn muốn tiếp tục thảo luận, có lẽ Trò chuyện Kỹ thuật phần mềm sẽ phù hợp hơn.
Caleb

1

Xem xét điều này

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

D0 bạn thực sự muốn viết

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

1

Các câu trả lời khác nhắm vào các trường hợp phổ biến hơn, nhưng có một lý do khác: Trong một số ngôn ngữ lập trình, nó có thể bị quá tải; vd: Scala .


Bài học Scala nhỏ:

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

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

Nếu một lớp chỉ định nghĩa +toán tử, x+=ythì thực sự là một phím tắt của x=x+y.

+=Tuy nhiên, nếu một lớp quá tải , thì chúng không:

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)”."

Ngoài ra, các toán tử ++=là hai toán tử riêng biệt (và không chỉ các toán tử này: + a, ++ a, a ++, a + ba + = b cũng là các toán tử khác nhau); trong các ngôn ngữ nơi quá tải toán tử có sẵn, điều này có thể tạo ra các tình huống thú vị. Giống như mô tả ở trên - nếu bạn sẽ quá tải +toán tử để thực hiện việc thêm, hãy nhớ rằng +=cũng sẽ phải bị quá tải.


"Nếu một lớp chỉ định nghĩa toán tử +, x + = y thực sự là một phím tắt của x = x + y." Nhưng chắc chắn nó cũng hoạt động theo cách khác? Trong C ++, trước tiên, nó rất phổ biến để định nghĩa +=và sau đó định nghĩa a+blà "tạo một bản sao a, đặt bnó bằng cách sử dụng +=, trả lại bản sao của a".
rẽ trái

1

Nói một lần và chỉ một lần: trong x = x + 1, tôi nói 'x' hai lần.

Nhưng đừng bao giờ viết, 'a = b + = 1' hoặc chúng ta sẽ phải giết 10 chú mèo con, 27 con chuột, một con chó và một con chuột đồng.


Bạn không bao giờ nên thay đổi giá trị của một biến, vì nó giúp dễ dàng chứng minh mã là chính xác - xem lập trình chức năng. Tuy nhiên nếu bạn làm thế, tốt hơn hết là đừng nói những điều chỉ một lần.


"Không bao giờ thay đổi giá trị của một biến" Mô hình chức năng ?
Peter Mortensen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.