Sẽ hiệu quả hơn khi sử dụng if-return-return hoặc if-other-return?


141

Giả sử tôi có một iftuyên bố với a return. Từ góc độ hiệu quả, tôi nên sử dụng

if(A > B):
    return A+1
return A-1

hoặc là

if(A > B):
    return A+1
else:
    return A-1

Tôi nên thích cái này hay cái khác khi sử dụng ngôn ngữ được biên dịch (C) hoặc ngôn ngữ được viết theo kịch bản (Python)?


11
Trong một ngôn ngữ được biên dịch, bạn không cần phải lo lắng về hiệu quả nhiều. Trình biên dịch sắp xếp ra. Bạn nên viết mã của bạn để bạn có thể đọc nó. (Bạn vẫn phải lo lắng về hiệu quả của các thuật toán của mình và việc sử dụng các loại cẩu thả, v.v. sẽ ảnh hưởng đến hiệu quả - bạn chỉ không lo lắng về phong cách của mình quá nhiều.) Mặc dù vậy tôi không biết về Python.
am

5
Dựa vào trình biên dịch của bạn để sắp xếp mã của bạn là một bước nguy hiểm - và yêu cầu một trình biên dịch không thể sai được. Tốt hơn nếu bạn biết ai muốn mã của bạn làm!
Andrew

1
Nếu những gì bạn đang làm được xác định bởi thông số kỹ thuật, thì tôi không tin có bất kỳ lý do nào để nghi ngờ trình biên dịch. Nó sẽ được viết là những người thông minh hơn bạn rất nhiều, và rất có thể bạn đã phạm sai lầm nhiều hơn họ.
sẽ

7
Làm thế nào điều này có thể được đóng lại cho ý kiến ​​dựa? Nó có thể là một ý kiến sau khi bạn biết rằng không có sự khác biệt hiệu suất giữa hai. Tôi đã không làm, và tôi khá chắc chắn rằng nhiều người cũng không.
Jorge Leitao

1
Mặc dù câu hỏi khá phổ biến, nhưng nó không thể được trả lời chính xác nếu không có ngôn ngữ cụ thể, hoặc nếu không, việc trả lời cho mọi ngôn ngữ sẽ quá dài cho định dạng này.
Emile Bergeron

Câu trả lời:


195

returncâu lệnh chấm dứt việc thực thi hàm hiện tại, hai dạng tương đương (mặc dù dạng thứ hai có thể đọc được nhiều hơn so với dạng thứ nhất).

Hiệu quả của cả hai hình thức là tương đương nhau, mã máy bên dưới phải thực hiện bước nhảy nếu ifđiều kiện là sai.

Lưu ý rằng Python hỗ trợ cú pháp cho phép bạn chỉ sử dụng một returncâu lệnh trong trường hợp của mình:

return A+1 if A > B else A-1

32
C cũng ủng hộ điều đó. return (A>B)?A+1:A-1;Tuy nhiên, hoàn toàn không có hiệu suất từ việc viết mã như thế này. Tất cả những gì chúng tôi đã đạt được là làm cho mã bị xáo trộn, không thể đọc được và trong một số trường hợp dễ bị tổn thương hơn đối với các chương trình khuyến mãi ngầm.
Lundin

47
@Lundin bị xáo trộn? không thể đọc được? Chỉ dành cho những người không biết toán tử ternary.
glglgl

6
@Lundin Theo lập luận này, <là thực tiễn tồi vì -1 < 1utạo ra kết quả không mong muốn.
glglgl

3
@glglgl: Không, bởi vì mọi người mong đợi toán tử ?: Hành xử như thể khác, điều đó không đúng. Nếu ai đó sẽ viết mã như -1 < 1u, mà tôi nghi ngờ, họ sẽ dễ dàng phát hiện ra lỗi. Tuy nhiên, khá nhiều người sẽ viết một số phiên bản mã tôi đã đăng. Tôi đã thấy các lỗi như vậy quá thường xuyên trong mã sản xuất để tin tưởng toán tử ?: Cũng như một quy tắc chung, nếu ngôn ngữ cung cấp cho bạn hai cách khác nhau để làm cùng một việc, chỉ sử dụng một trong số chúng, đừng chọn ngẫu nhiên một trong hai cách tùy thuộc vào tâm trạng của bạn.
Lundin

6
@Lundin đó là một đối số cho việc cẩn thận với ?: Trong C, nhưng dường như bạn đang nói nó cũng áp dụng cho Python. Bạn có thể chỉ ra bất kỳ ví dụ nào trong đó việc sử dụng ternary trong Python dẫn đến kết quả không mong muốn không?
lvc

33

Từ hướng dẫn về kiểu dáng của Chromium :

Không sử dụng khác sau khi trở về:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2

1
Cảm ơn. +1. Tôi có thể hỏi tại sao không sử dụng khác sau khi trở về?
Tim

1
if-other tương đương về chức năng, nhưng nó dài dòng. Cái khác là không cần thiết.
skeller88

17
Tôi đã ngạc nhiên vì lần đầu tiên có vẻ rõ ràng hơn và do đó tốt hơn.
Tim

4
Bạn có thể làm một trường hợp hợp lý cho một trong hai. Điều quan trọng nhất trong quyết định này IMO là phải nhất quán trong cơ sở mã.
skeller88

2
có lẽ bạn sẽ tìm thấy trong phần lớn các trường hợp if-else-returncác nhánh gần như không bao giờ bằng nhau (nếu có, thì dù sao bạn cũng nên tái cấu trúc; sử dụng switchcấu trúc hoặc cho Python, liệt kê một lệnh / sử dụng lệnh có thể gọi / v.v.). Do đó, hầu hết tất cả if-else-returnđều là trường hợp của các mệnh đề bảo vệ và chúng luôn có thể kiểm tra được (giả định biểu thức đã kiểm tra) mà không có else.
cowbert

5

Về phong cách mã hóa:

Hầu hết các tiêu chuẩn mã hóa cho dù ngôn ngữ cấm nhiều câu lệnh trả về từ một chức năng là thực tiễn xấu.

(Mặc dù cá nhân tôi sẽ nói rằng có một số trường hợp trong đó nhiều câu lệnh trả về có ý nghĩa: trình phân tích cú pháp giao thức văn bản / dữ liệu, các hàm có xử lý lỗi mở rộng, v.v.)

Sự đồng thuận từ tất cả các tiêu chuẩn mã hóa công nghiệp là biểu thức nên được viết là:

int result;

if(A > B)
{
  result = A+1;
}
else
{
  result = A-1;
}
return result;

Về hiệu quả:

Ví dụ trên và hai ví dụ trong câu hỏi hoàn toàn tương đương về hiệu quả. Mã máy trong tất cả các trường hợp này phải so sánh A> B, sau đó phân nhánh với phép tính A + 1 hoặc A-1, sau đó lưu kết quả của điều đó vào một thanh ghi CPU hoặc trên ngăn xếp.

BIÊN TẬP :

Nguồn:

  • MISRA-C: 2004 quy tắc 14.7, lần lượt trích dẫn ...:
  • IEC 61508-3. Phần 3, bảng B.9.
  • IEC 61508-7. C.2.9.

37
Bạn có chắc rằng tôn giáo một lần đã lây nhiễm hầu hết các tiêu chuẩn mã hóa? Điều đó thật đáng sợ.
Daniel Fischer

7
Tôi muốn nói rằng quy tắc không có ý nghĩa hầu hết thời gian. Tôi có xu hướng tìm mã dễ đọc hơn và dễ theo dõi hơn với lợi nhuận tại các điểm thích hợp. Nhưng đó chỉ là tôi. Tuy nhiên, tôi nghĩ về các tiêu chuẩn mã hóa của mỗi công ty / dự án, không phải những thứ như MISRA khi mà các đơn thuốc ngu ngốc đôi khi có thể có một số giá trị. Tôi hy vọng hầu hết đã không mua vào ý tưởng điểm duy nhất.
Daniel Fischer

3
@DanielFischer: Trong tiêu chuẩn mã hóa C dựa trên MISRA mà tôi thiết kế cho công ty của mình, tôi có quy tắc "Một hàm sẽ chỉ có một điểm thoát duy nhất, ở cuối hàm, trừ khi một điểm thoát duy nhất tạo mã ít đọc hơn ". Vì vậy, nó là MISRA-C nhưng ngoại lệ cho quy tắc. Nếu bạn viết một hàm phân tích cú pháp nâng cao có thể trả về, giả sử có 10 lỗi khác nhau, mức độ của các dấu ngoặc lồng làm cho mã hoàn toàn không thể đọc được - trong trường hợp như vậy sẽ trở nên hợp lý hơn khi trả về ngay lập tức khi gặp lỗi.
Lundin

6
Xem câu hỏi SO này để thảo luận và liên kết thêm để thảo luận thêm về vấn đề một điểm thoát. Bên cạnh quy tắc một điểm thoát là lỗi thời và quá "kỹ thuật", Python đặc biệt khuyến khích một quan điểm "phẳng tốt hơn lồng nhau" và đặt return bất cứ nơi nào nó rõ ràng là cách thành ngữ trong Python.
John Y

1
@percebus Tôi hoàn toàn đồng ý và độ phức tạp theo chu kỳ là một lý lẽ tốt để chống lại sự trở lại đơn lẻ. Và tôi đã chọc vào ủy ban MISRA về điều này nhiều lần, ví dụ như thấy điều này . Ít nhất là quy tắc đã bị hạ cấp xuống tư vấn trong MISRA-C: 2012.
Lundin

3

Với bất kỳ trình biên dịch hợp lý, bạn sẽ quan sát không có sự khác biệt; chúng nên được biên dịch thành mã máy giống hệt nhau vì chúng tương đương nhau.


2

Đây là một câu hỏi về phong cách (hoặc sở thích) vì thông dịch viên không quan tâm. Cá nhân tôi sẽ cố gắng không đưa ra tuyên bố cuối cùng của hàm trả về giá trị ở mức thụt lề khác với cơ sở hàm. Cái khác trong ví dụ 1 che khuất, nếu chỉ một chút, nơi kết thúc của hàm là.

Theo sở thích tôi sử dụng:

return A+1 if (A > B) else A-1

Vì nó tuân theo cả quy ước tốt là có một câu trả về duy nhất là câu lệnh cuối cùng trong hàm (như đã đề cập) và mô hình lập trình chức năng tốt để tránh kết quả trung gian kiểu bắt buộc.

Đối với các hàm phức tạp hơn, tôi thích chia hàm thành nhiều hàm phụ để tránh trả về sớm nếu có thể. Mặt khác, tôi trở lại sử dụng một biến kiểu bắt buộc được gọi là rval. Tôi cố gắng không sử dụng nhiều câu lệnh return trừ khi hàm này không quan trọng hoặc câu lệnh return trước khi kết thúc là kết quả của một lỗi. Trở về sớm làm nổi bật thực tế là bạn không thể tiếp tục. Đối với các hàm phức tạp được thiết kế để phân nhánh thành nhiều hàm con, tôi cố gắng mã hóa chúng dưới dạng các câu lệnh tình huống (được điều khiển bởi một dict chẳng hạn).

Một số áp phích đã đề cập đến tốc độ hoạt động. Tốc độ thời gian chạy là thứ yếu đối với tôi vì nếu bạn cần tốc độ thực thi thì Python không phải là ngôn ngữ tốt nhất để sử dụng. Tôi sử dụng Python vì hiệu quả của mã hóa (tức là viết mã không có lỗi) quan trọng với tôi.


1
Nếu một người dùng sẽ bỏ phiếu xuống câu trả lời của tôi, tôi sẽ đánh giá cao một nhận xét về lý do tại sao họ nghĩ rằng tôi sai.
Stephen Ellwood

Tôi có lẽ sẽ chỉ là một dòng trước để làm cho nó 1 dòng trên mỗi câu lệnh cho mục đích dễ đọc. var n = 1 if (A > B) else -1 return A+n
xe buýt

@percebus trong một số trường hợp tôi sẽ đồng ý nếu tên biến có thể nâng cao ý nghĩa. Ví dụ: 'code' move_x = 1 nếu my_x <đối thủ_x khác -1 # di chuyển về phía đối thủ
Stephen Ellwood

BTW Tôi thực sự nâng cao câu trả lời của bạn. Nếu bạn thấy câu trả lời của tôi khá giống nhau
thì

2

Cá nhân tôi tránh elsecác khối khi có thể. Xem Chiến dịch chống nếu

Ngoài ra, họ không tính phí 'thêm' cho dòng, bạn biết: p

"Đơn giản là tốt hơn phức tạp" & "Dễ đọc là vua"

delta = 1 if (A > B) else -1
return A + delta

2
Tại sao bỏ phiếu xuống? Là một câu trả lời 'pythonic'. Bạn có thể không coi đó là một câu trả lời ưa thích. Nhưng không phải là một không hợp lệ. Tôi cũng đang tuân theo Nguyên tắc KISS en.wikipedia.org/wiki/KISS_principl
Sensebus

3
Tôi đánh giá cao câu trả lời của bạn vì đối với tôi, nó đạt điểm dễ đọc và đơn giản. Cá nhân tôi thấy khó chịu khi có ai đó bỏ phiếu cho tôi mà không cho tôi biết lý do tại sao câu trả lời của tôi lại chủ động phủ định.
Stephen Ellwood

1
Trước đây không nghe về chiến dịch Anti-if nhưng có thể hiểu tại sao ifs có thể nguy hiểm. Tôi luôn cố gắng giới hạn số lượng mã được bao quanh bởi một câu lệnh if và cố gắng viết lại cây elif để sử dụng dict. Điều này là nhận được một chút lạc đề mặc dù.
Stephen Ellwood

1
@StephenEllwood Sử dụng dicts để tránh diffs là một ý tưởng rất tệ về hiệu suất.
Bachsau

@Bachsau Có lẽ bạn đúng. Tôi chưa bao giờ phải lo lắng về hiệu suất vì tất cả các tập lệnh của tôi chạy trong vài giây. Đối với tôi khả năng đọc thường hơn hẳn hiệu suất. Vì tôi không phải là lập trình viên toàn thời gian; chúng chỉ là một phương tiện để kết thúc.
Stephen Ellwood

1

Phiên bản A đơn giản hơn và đó là lý do tại sao tôi sẽ sử dụng nó.

Và nếu bạn bật tất cả các cảnh báo của trình biên dịch trong Java, bạn sẽ nhận được cảnh báo trên Phiên bản thứ hai vì nó không cần thiết và làm tăng độ phức tạp của mã.


1

Tôi biết câu hỏi được gắn thẻ python, nhưng nó đề cập đến các ngôn ngữ động vì vậy tôi nghĩ rằng tôi nên đề cập rằng trong ruby, câu lệnh if thực sự có kiểu trả về để bạn có thể làm gì đó như

def foo
  rv = if (A > B)
         A+1
       else
         A-1
       end
  return rv 
end

Hoặc bởi vì nó cũng có sự trở lại ngầm

def foo 
  if (A>B)
    A+1
  else 
    A-1
  end
end

mà xung quanh vấn đề phong cách của việc không có nhiều lợi nhuận khá độc đáo.

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.