Cách thoát khỏi mệnh đề if


104

Có những loại phương pháp nào để thoát sớm một ifmệnh đề?

Đôi khi tôi đang viết mã và muốn đặt một breakcâu lệnh bên trong một ifmệnh đề, chỉ để nhớ rằng chúng chỉ có thể được sử dụng cho các vòng lặp.

Hãy lấy đoạn mã sau làm ví dụ:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
   # more code here

Tôi có thể nghĩ ra một cách để làm điều này: giả sử các trường hợp thoát xảy ra trong các câu lệnh if lồng nhau, hãy bọc đoạn mã còn lại trong một khối khác lớn. Thí dụ:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   else:
       ...
       if condition_b:
           # do something
           # and then exit the outer if block
       else:
           # more code here

Vấn đề với điều này là nhiều vị trí thoát hơn có nghĩa là nhiều mã lồng / thụt lề hơn.

Ngoài ra, tôi có thể viết mã của mình để các ifmệnh đề càng nhỏ càng tốt và không yêu cầu bất kỳ lối thoát nào.

Có ai biết một cách tốt / tốt hơn để thoát khỏi một ifmệnh đề?

Nếu có bất kỳ mệnh đề else-if và else liên quan nào, tôi nghĩ rằng việc thoát ra sẽ bỏ qua chúng.


2
Đối với mẫu mã thứ hai của bạn - bạn có biết về elif?
Craig McQueen

2
"Ngoài ra, tôi có thể viết mã của mình để có mệnh đề if càng nhỏ càng tốt và không yêu cầu bất kỳ lối thoát nào." - và chắc chắn đây sẽ là hành động tốt nhất. :-)
Michał Marczyk

2
@Craig McQueen: Tôi có, nhưng nói rằng tôi muốn có mã thực thi giữa các câu lệnh điều kiện? Ví dụ: if a: #stuff; #stuff_inbetween; if b: #stuff;mã inbetween phụ thuộc vào not anhưng không phụ thuộc vào b.
Roman

xin chào, xin đừng quên elif stackoverflow.com/a/2069680/7045119
kerbrose

Câu trả lời:


99

(Phương pháp này hoạt động với ifs, nhiều vòng lặp lồng nhau và các cấu trúc khác mà bạn không thể breakdễ dàng thực hiện được.)

Gói mã trong chức năng riêng của nó. Thay vì break, sử dụng return.

Thí dụ:

def some_function():
    if condition_a:
        # do something and return early
        ...
        return
    ...
    if condition_b:
        # do something else and return early
        ...
        return
    ...
    return

if outer_condition:
    ...
    some_function()
    ...

4
Tôi rất vui khi bổ sung vào túi các thủ thuật lập trình viên của bạn. Theo kinh nghiệm của tôi, cách tiếp cận đó hoạt động gần như bất cứ khi nào bạn bị cám dỗ sử dụng goto tiến lên. (Và nó cả gợi ý tại và địa chỉ các tình huống mà một chức năng duy nhất là nhận được quá lớn)
Drew Dormann

2
Lý tưởng nhất là bạn có thể đạt được cả hai, nhưng có những lúc bạn phải giao dịch mã tốt để có hiệu suất tốt. Những thời điểm đó rất hiếm, đặc biệt là khi bạn đang cân nhắc sử dụng Python. Nói cách khác: đừng quá lo lắng về chi phí cuộc gọi hàm.
ephemient

17
Có một giai thoại cũ: "Dennis Ritchie khuyến khích tính mô-đun bằng cách nói với tất cả và lặt vặt rằng các lệnh gọi hàm thực sự, thực sự rẻ trong C. Mọi người bắt đầu viết các hàm nhỏ và mô đun hóa. Nhiều năm sau, chúng tôi phát hiện ra rằng các lệnh gọi hàm vẫn còn đắt trên PDP-11 và mã VAX thường dành 50% thời gian của nó trong hướng dẫn CALLS. Dennis đã nói dối chúng tôi! Nhưng đã quá muộn; tất cả chúng tôi đã mắc câu ... "
ephemient

1
@ephemient: Thật là buồn cười, câu chuyện có nhiều điều hơn không? Tôi muốn đọc toàn bộ.
Roman

4
Trích dẫn này trích từ chương 4 của cuốn sách Nghệ thuật lập trình Unix (trực tuyến tại faqs.org/docs/artu ). Bạn thực sự nên đọc toàn bộ, nếu bạn chưa từng đọc.
ephemient

55
từ goto nhập goto, nhãn

if some_condition:
   ...
   if condition_a:
       # làm việc gì đó
       # và sau đó thoát khỏi khối if bên ngoài
       goto .end
   ...
   if condition_b:
       # làm việc gì đó
       # và sau đó thoát khỏi khối if bên ngoài
       goto .end
   # mã khác ở đây

nhãn .end

(Xin đừng thực sự sử dụng cái này.)


35
+1 vì điều này thật buồn cười. Một tìm kiếm trên google đã tiết lộ cho tôi rằng đây là một mô-đun trò đùa ngày Cá tháng Tư.
Roman

2
Tôi cũng đã liên kết với nó. Nhấp vào đầu tiên goto.
ephemient

1
này làm tôi nhớ mã lắp ráp với tất cả các loại phân nhánh :)
phunehehe

2
@ephemient: À, không nhận thấy liên kết. Tìm thấy đó là mã đánh dấu. Nhưng bây giờ tôi nhìn vào mã của bạn, tôi không thấy bất kỳ đánh dấu thực sự nào đang diễn ra ..
Roman

1
Khi PHP giới thiệu goto, tôi đã chuyển sang Python php.net/manual/en/control-structures.goto.php
Marc

25
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break

3
Ồ, tôi rất thích ý tưởng này. Tôi nghĩ rằng điều này chính xác giải quyết được mong muốn ban đầu của tôi. Tuy nhiên, tôi có cảm giác khó chịu rằng đây không phải là cách làm tốt (và vì lý do đó, tôi sẽ giữ lại câu trả lời được chấp nhận hiện tại vì nó thúc đẩy phong cách viết mã tốt).
Roman

6
Lưu ý rằng bạn có thể giữ bản gốc nếu và bọc toàn bộ trong một while True:. Chỉ cần đảm bảo đặt một breaktuyên bố ở cuối! Đối với các ngôn ngữ với các do-while xây dựng, nó là idomatic nhiều việc phải làm:do { code that can conditionally break out } while (false);
Thomas Việc chỉnh sửa

10

Bạn có thể mô phỏng chức năng của goto với các ngoại lệ:

try:
    # blah, blah ...
    # raise MyFunkyException as soon as you want out
except MyFunkyException:
    pass

Tuyên bố từ chối trách nhiệm: Tôi chỉ muốn lưu ý đến bạn khả năng làm mọi việc theo cách này, trong khi tôi không xác nhận điều đó là hợp lý trong các trường hợp bình thường. Như tôi đã đề cập trong một bình luận về câu hỏi, cấu trúc mã để tránh các điều kiện Byzantine ngay từ đầu là tốt hơn cho đến nay. :-)


Haha, tôi thích giải pháp sáng tạo này. Mặc dù tôi sẽ tuân theo tuyên bố từ chối trách nhiệm của bạn và không sử dụng mã vui nhộn như vậy.
Roman

@Roman: Rất vui khi thêm một thủ thuật khác cùng với Shmoopty - ngay cả khi tôi cảm thấy nghịch ngợm khi so sánh. ;-)
Michał Marczyk

8

có lẽ điều này?

if some_condition and condition_a:
       # do something
elif some_condition and condition_b:
           # do something
           # and then exit the outer if block
elif some_condition and not condition_b:
           # more code here
else:
     #blah
if

1
Vâng, điều đó có thể hoạt động. Tôi nghĩ rằng tâm trí của eliftôi đã trở nên trống rỗng trong khi tôi viết nó. Mặc dù tôi nghĩ điều này sẽ không hoạt động trong trường hợp tôi muốn mã thực thi ở giữa các câu lệnh if lồng nhau.
Roman

đây thực sự là câu trả lời chính xác. sao tôi không thấy mọi người giới thiệu cái này ?? :)
kerbrose

6

Đối với những gì thực sự được hỏi, cách tiếp cận của tôi là đặt những thứ đó ifvào trong một vòng lặp một vòng

while (True):
    if (some_condition):
        ...
        if (condition_a):
            # do something
            # and then exit the outer if block
            break
        ...
        if (condition_b):
            # do something
            # and then exit the outer if block
            break
        # more code here
    # make sure it is looped once
    break

Kiểm tra nó:

conditions = [True,False]
some_condition = True

for condition_a in conditions:
    for condition_b in conditions:
        print("\n")
        print("with condition_a", condition_a)
        print("with condition_b", condition_b)
        while (True):
            if (some_condition):
                print("checkpoint 1")
                if (condition_a):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 2")
                    break
                print ("checkpoint 3")
                if (condition_b):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 4")
                    break
                print ("checkpoint 5")
                # more code here
            # make sure it is looped once
            break

1
Thành thật mà nói, đây là giải pháp sạch sẽ nhất. Rất rõ ràng khi nào mã thoát ra - bạn không phải lo lắng về phạm vi chức năng bên trong các chức năng và không có hiệu suất hoặc nợ logic.
Trent

2
Có thể xem xét sử dụng for _ in range(1):thay vì while True:. (1) Better giao tiếp ý định của một vòng lặp đơn và (2) không có lệnh break cuối cùng để thoát khỏi vòng lặp (có thể được gỡ bỏ một cách tình cờ sau này)
Bernhard Kausler

3

Nói chung là không. Nếu bạn đang lồng "ifs" và phá vỡ chúng, bạn đang làm sai.

Tuy nhiên, nếu bạn phải:

if condition_a:
   def condition_a_fun():
       do_stuff()
       if we_wanna_escape:
           return
   condition_a_fun()
if condition_b:
   def condition_b_fun():
       do_more_stuff()
       if we_wanna_get_out_again:
           return
   condition_b_fun()

Lưu ý, các hàm không PHẢI được khai báo trong câu lệnh if, chúng có thể được khai báo trước;) Đây sẽ là một lựa chọn tốt hơn, vì nó sẽ tránh cần phải cấu trúc lại một if / then xấu xí sau này.


Cảm ơn vì câu trả lời. Tôi không chắc ý của bạn về 'vòng lặp lồng nhau'. Nếu bạn đang đề cập đến việc tôi đề cập đến từ khóa 'break', tôi chỉ đơn giản là đang cố gắng thúc đẩy tìm kiếm của tôi cho if-exit bằng cách so sánh nó với sự tồn tại của lối ra vòng lặp. Ngoài ra, tôi không chắc mã của bạn giải quyết vấn đề như thế nào, vì ví dụ của tôi đã có if condition_avà được if condition_blồng bên trong một if some_condition. Tôi muốn có thể thoát ra khỏi if some_condition.
Roman

Lý do mọi người muốn lồng ifs và phá vỡ chúng có lẽ không phải vì họ làm sai mà có thể vì họ muốn viết mã đơn giản và sạch sẽ KHÔ. trường hợp đơn giản là khi chương trình cần thực hiện x () khi cả condition_a và condition_b đều được đáp ứng và chỉ cần thực hiện y () đối với condition_a và chỉ cần thực hiện z () đối với condition_b. và lập trình viên từ chối viết x () nhiều lần
izzulmakin

1

Hiệu quả những gì bạn đang mô tả là các câu lệnh goto, thường được xoay quanh khá nhiều. Ví dụ thứ hai của bạn dễ hiểu hơn nhiều.

Tuy nhiên, sạch hơn vẫn sẽ là:

if some_condition:
   ...
   if condition_a:
       your_function1()
   else:
       your_function2()

...

def your_function2():
   if condition_b:
       # do something
       # and then exit the outer if block
   else:
       # more code here

1

Có một cách khác không dựa vào việc xác định các hàm (vì đôi khi điều đó khó đọc hơn đối với các đoạn mã nhỏ), không sử dụng thêm vòng lặp while bên ngoài (có thể cần được đánh giá đặc biệt trong các nhận xét để thậm chí có thể hiểu ngay từ cái nhìn đầu tiên) , không sử dụng goto (...) và quan trọng nhất là bạn phải giữ mức thụt lề của mình cho bên ngoài nếu bạn không phải bắt đầu lồng nội dung.

if some_condition:
   ...
   if condition_a:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if: # if and only if exit_if wasn't set we want to execute the following code
   # keep doing something
   if condition_b:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if:
   # keep doing something

Vâng, điều đó cũng cần một cái nhìn thứ hai để có thể đọc được, tuy nhiên, nếu các đoạn mã nhỏ, điều này không yêu cầu phải theo dõi bất kỳ vòng lặp while nào sẽ không bao giờ lặp lại và sau khi hiểu các ifs trung gian là gì, nó có thể dễ dàng đọc được, tất cả đều có trong một chỗ và cùng một chỗ thụt vào.

Và nó sẽ khá hiệu quả.


0

Vì vậy, ở đây tôi hiểu rằng bạn đang cố gắng thoát ra khỏi khối mã if bên ngoài

if some_condition:
    ...
    if condition_a:
       # do something
       # and then exit the outer if block
       ...
    if condition_b:
       # do something
       # and then exit the outer if block
# more code here

Một cách giải quyết vấn đề này là bạn có thể kiểm tra điều kiện sai trong khối if bên ngoài, sau đó sẽ hoàn toàn thoát ra khỏi khối mã, sau đó bạn sử dụng một khối else để lồng các if khác để làm gì đó

if test_for_false:
    # Exit the code(which is the outer if code)

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something

0

Điều duy nhất có thể áp dụng điều này mà không cần các phương pháp bổ sung là elifví dụ sau

a = ['yearly', 'monthly', 'quartly', 'semiannual', 'monthly', 'quartly', 'semiannual', 'yearly']
# start the condition
if 'monthly' in b: 
    print('monthly') 
elif 'quartly' in b: 
    print('quartly') 
elif 'semiannual' in b: 
    print('semiannual') 
elif 'yearly' in b: 
    print('yearly') 
else: 
    print('final') 

-1

Đây là một cách khác để xử lý điều này. Nó sử dụng một mục duy nhất cho vòng lặp cho phép bạn chỉ sử dụng continue. Nó ngăn chặn nhu cầu không cần thiết phải có các chức năng phụ mà không có lý do. Và cũng loại bỏ các vòng lặp while vô hạn tiềm năng.

if something:
    for _ in [0]:
        # Get x
        if not x:
            continue

        # Get y
        if not y:
            continue

        # Get z
        if not z:
            continue

        # Stuff that depends on x, y, and z

-2

sử dụng returntrong điều kiện if sẽ trả về bạn khỏi hàm, vì vậy bạn có thể sử dụng return để phá vỡ điều kiện if.


2
anh ta muốn phá vỡ nếu không thoát khỏi chức năng
StealthOne
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.