2010-11-13 32 views
10

xem xét bạn có một số biểu hiện nhưBài tập "Đôi" - nên tránh không?

i = j = 0 

giả này được xác định rõ trong ngôn ngữ của bạn lựa chọn. Nói chung, tốt hơn là nên chia nhỏ thành hai biểu thức như

i = 0 
j = 0 

Tôi thấy điều này đôi khi trong mã thư viện. Nó dường như không mua cho bạn nhiều về ngắn gọn và không nên thực hiện bất kỳ tốt hơn so với hai báo cáo (mặc dù đó có thể là trình biên dịch phụ thuộc). Vì vậy, có một lý do để sử dụng một trong những khác? Hay nó chỉ là sở thích cá nhân? Tôi biết điều này nghe có vẻ giống như một câu hỏi ngớ ngẩn nhưng nó bugging tôi trong một thời gian dài bây giờ :-).

Trả lời

3

Hai biểu mẫu phản ánh các quan điểm khác nhau về nhiệm vụ.

Trường hợp đầu tiên xử lý nhiệm vụ (ít nhất là trường hợp bên trong) làm toán tử (hàm trả về giá trị).

Trường hợp thứ hai coi nhiệm vụ là câu lệnh (lệnh để thực hiện điều gì đó).

Có một số trường hợp phép gán với tư cách người vận hành có điểm, chủ yếu là ngắn gọn hoặc sử dụng trong ngữ cảnh mong đợi kết quả. Tuy nhiên tôi cảm thấy nó khó hiểu. Vì một vài lý do:

  • Nhà điều hành chuyển nhượng về cơ bản là các nhà khai thác tác dụng phụ, và hiện tại, việc tối ưu hóa chúng cho trình biên dịch là vấn đề. Trong các ngôn ngữ như C và C++, chúng dẫn đến nhiều trường hợp Hành vi không xác định hoặc mã không được tối ưu hóa.
  • Không rõ phải trả lại những gì. Toán tử gán nên trả về giá trị được gán, hoặc nó trả về địa chỉ của địa điểm mà nó đã được lưu trữ. Một hoặc khác có thể hữu ích, tùy thuộc vào ngữ cảnh.
  • Với các phép gán tổng hợp như +=, thậm chí còn tồi tệ hơn. Không rõ liệu toán tử có trả về giá trị ban đầu, kết quả được kết hợp hay thậm chí là nơi nó được lưu trữ.

Việc chuyển nhượng dưới dạng câu lệnh đôi khi dẫn đến biến trung gian, nhưng đó là nhược điểm duy nhất tôi thấy. Rõ ràng và trình biên dịch biết cách tối ưu hóa hiệu quả các câu lệnh như vậy.

Về cơ bản, tôi sẽ tránh chuyển nhượng với tư cách người điều hành bất cứ khi nào có thể. Các trường hợp được trình bày là rất đơn giản và không thực sự khó hiểu, nhưng như một quy tắc chung tôi vẫn thích.

i = 0 
j = 0 

hoặc

i, j = 0, 0 

cho các ngôn ngữ hỗ trợ, chuyển nhượng song song.

1

Cách thứ hai dễ đọc hơn và rõ ràng hơn, tôi thích nó hơn.

Tuy nhiên tôi cố gắng tránh "kép" tuyên bố:

int i, j; 

thay vì

int i; 
int j; 

nếu họ sẽ liên tiếp. Đặc biệt trong trường hợp của MyVeryLong.AndComplexType

+1

Chưa kể 'char * i, j; 'so với' char * i, * j; '. –

+0

@Ignacio: Câu hỏi hay về liên quan đến chủ đề của bạn http://stackoverflow.com/questions/377164/whats-your-preferred-pointer-declaration-style-and-why – abatishchev

2

Nó phụ thuộc vào ngôn ngữ. Trong các ngôn ngữ hướng đối tượng cao, kết quả phân bổ kép trong cùng một đối tượng được gán cho nhiều biến, vì vậy các thay đổi trong một biến được phản ánh trong biến khác.

$ python -c 'a = b = [] ; a.append(1) ; print b' 
[1] 
+0

trong các trường hợp như vậy 'b = []; a = b' là cú pháp rõ ràng hơn – kriss

+0

@kriss: Nhưng câu hỏi không hỏi về cú pháp đó. –

+0

Tôi đã chỉ ra rằng tương đương với một dòng kết hợp gán không được gán cùng một hằng số cho cả hai objets. Điều này dựa trên quy ước rằng kết quả của phép gán là giá trị được gán cho biến đầu tiên. Đây là một sự lựa chọn có thể xảy ra đối với nhiều người. Trong các ngôn ngữ hỗ trợ các lệnh ngầm định (như C++), nó không rõ ràng nếu giá trị trả về phải là đối tượng trước khi diễn viên diễn ra (tôi tin nó là cái đầu tiên, nhưng phải kiểm tra tài liệu tham khảo chuẩn chắc chắn rồi). – kriss

2

Hầu hết mọi người sẽ tìm thấy cả hai khả năng đều có thể đọc được. Một số người trong số những người này sẽ có một sở thích cá nhân cho một trong hai cách. Nhưng có những người có thể, ngay từ cái nhìn đầu tiên, bị lẫn lộn bởi "sự phân công kép". Cá nhân tôi thích cách tiếp cận riêng biệt, bacause

  • Đó là 100% có thể đọc được
  • Nó không phải là thực sự tiết so với các biến thể đôi
  • Nó cho phép tôi quên các quy tắc của associativity cho toán tử =
8

Ngày xửa ngày xưa có sự khác biệt về hiệu suất, đó là một trong những lý do mà loại nhiệm vụ này được sử dụng. Các trình biên dịch sẽ biến i = 0; j = 0; thành:

load 0 
store [i] 
load 0 
store [j] 

Vì vậy, bạn có thể tiết kiệm một lệnh bằng cách sử dụng i = j = 0 như trình biên dịch sẽ tắt chức năng này vào:

load 0 
store [j] 
store [i] 

Ngày nay các trình biên dịch có thể làm kiểu này optimisations tự. Ngoài ra, khi các CPU hiện tại chạy một số hướng dẫn cùng một lúc, hiệu suất có thể không còn đơn giản được đo bằng số lượng hướng dẫn nữa. Hướng dẫn trong đó một hành động không dựa vào kết quả của một hành động khác có thể chạy song song, do đó, phiên bản sử dụng giá trị riêng biệt cho mỗi biến có thể thực sự nhanh hơn.

Về kiểu lập trình, bạn nên sử dụng cách thể hiện tốt nhất ý định của mã.

Bạn có thể ví dụ chuỗi các bài tập khi bạn chỉ muốn xóa một số biến và đặt các bài tập riêng biệt khi giá trị có ý nghĩa cụ thể. Đặc biệt là nếu ý nghĩa của việc thiết lập một biến cho giá trị khác với việc đặt biến khác thành cùng một giá trị.

3

Thứ nhất, ở cấp độ ngữ nghĩa, tùy thuộc vào việc bạn muốn nói rằng ij có cùng giá trị hay chỉ xảy ra cho cả hai đều có cùng giá trị.

Ví dụ: nếu ij là các chỉ mục thành một mảng 2D, cả hai đều bắt đầu bằng 0. j = i = 0 nói i bắt đầu ở số không và j bắt đầu từ nơi i bắt đầu. Nếu bạn muốn bắt đầu ở hàng thứ hai, bạn sẽ không nhất thiết muốn bắt đầu ở cột thứ hai, vì vậy tôi sẽ không khởi tạo cả hai trong cùng một câu lệnh - các chỉ mục cho các hàng và cột độc lập xảy ra cho cả hai bắt đầu bằng 0.

Ngoài ra, trong ngôn ngữ mà ij đại diện cho đối tượng phức tạp hơn là các biến không thể thiếu, hoặc trong trường hợp chuyển nhượng có thể gây ra một chuyển đổi ngầm, họ không tương đương:

#include <iostream> 

class ComplicatedObject 
{ 
public: 
    const ComplicatedObject& operator= (const ComplicatedObject& other) { 
     std::cout << " ComplicatedObject::operator= (const ComplicatedObject&)\n"; 
     return *this; 
    } 
    const ComplicatedObject& operator= (int value) { 
     std::cout << " ComplicatedObject::operator= (int)\n"; 
     return *this; 
    } 

}; 

int main() 
{ 
    { 
     // the functions called are not the same 
     ComplicatedObject i; 
     ComplicatedObject j; 

     std::cout << "j = i = 0:\n"; 
     j = i = 0; 

     std::cout << "i = 0; j = 0:\n"; 
     i = 0; 
     j = 0; 
    } 

    { 
     // the result of the first assignment is 
     // effected by implicit conversion 
     double j; 
     int i; 

     std::cout << "j = i = 0.1:\n"; 
     j = i = 0.1; 

     std::cout << " i == " << i << '\n' 
        << " j == " << j << '\n' 
        ; 

     std::cout << "i = 0.1; j = 0.1:\n"; 
     i = 0.1; 
     j = 0.1; 

     std::cout << " i == " << i << '\n' 
        << " j == " << j << '\n' 
        ; 
    } 

} 
Các vấn đề liên quan