برنامه‌نویسی پایتون فهرست مطالب
فصل ۸

حلقه while

حلقه for وقتی خوب است که از قبل بدانید چند بار باید تکرار کنید. ولی خیلی وقت‌ها نمی‌دانید: تا وقتی کاربر خسته شود، تا وقتی جواب درست پیدا شود، تا وقتی فایل تمام شود. اینجا جای while است.

در این فصل می‌خوانید
  1. اولین while
  2. حلقه بی‌پایان
  3. for یا while، کدام
  4. while True و break
  5. دستور continue
  6. میانگین در حال اجرا
  7. اعتبارسنجی ورودی
  8. بازی حدس عدد
  9. تشخیص عدد اول
  10. بلوک else حلقه
  11. باز کردن یک عدد رقم به رقم

۱. اولین while

while یعنی «تا وقتی که». شرطی می‌گیرد و تا وقتی آن شرط درست است، بدنه‌اش را تکرار می‌کند.

# A 'while' repeats as long as its condition is True.
# Something inside the loop MUST eventually make the condition False,
# otherwise the program never stops (an "infinite loop").
x = 0

while x < 10:
    x += 1          # without this line the loop would never end
    print(x)

شمارش با while: نمونه 01_first_while.py

شرح مثال

۲. حلقه بی‌پایان

اگر برنامه گیر کرد

حلقه بی‌پایان یعنی حلقه‌ای که شرطش هرگز نادرست نمی‌شود. برنامه ظاهراً هنگ می‌کند یا بی‌نهایت خط چاپ می‌کند. برای متوقف کردنش در ترمینال کلیدهای Ctrl + C را بزنید. این کلید را در همین مرحله یاد بگیرید، چون در هفته اول کار با while چند بار لازمتان می‌شود.

سه علت رایج حلقه بی‌پایان:

۳. for یا while، کدام

# Use 'for' when you know in advance how many turns you need.
for i in range(1, 6):
    print(i, end=' ')
print()

# Use 'while' when the number of turns depends on something that happens
# during the loop.
i = 1
while i <= 5:
    print(i, end=' ')
    i += 1
print()

یک کار، دو روش: نمونه 02_while_vs_for.py

وقتی کهحلقه مناسبمثال
تعداد تکرار از قبل معلوم استforچاپ ۱ تا ۱۰۰، پیمایش یک لیست
تعداد تکرار معلوم نیستwhileتا وقتی کاربر exit بزند، تا وقتی رمز درست شود

در مثال بالا هر دو یک خروجی دارند، ولی for کوتاه‌تر و کم‌خطرتر است، چون شمارنده را خودش مدیریت می‌کند و امکان حلقه بی‌پایان ندارد. قانون ساده: اگر می‌شود با for نوشت، با for بنویسید.

۴. while True و break

الگویی که در برنامه‌های واقعی بیش از همه دیده می‌شود این است: حلقه را عمداً بی‌پایان می‌سازیم و با break از وسطش بیرون می‌پریم. مقداری که باعث خروج می‌شود را نگهبان می‌نامند.

# A "sentinel" is a special value that means "stop".
# Here the sentinel is the word 'exit'.
total = 0

while True:                              # loop for ever ...
    user_input = input('Enter a number (or "exit"): ')

    if user_input == 'exit':
        break                            # ... until break jumps out of it

    number = float(user_input)
    total += number
    print('Sum is:', total)

print('Final sum:', total)

جمع تا رسیدن نگهبان: نمونه 03_sentinel_sum.py

Enter a number (or "exit"): 5
Sum is: 5.0
Enter a number (or "exit"): 3
Sum is: 8.0
Enter a number (or "exit"): exit
Final sum: 8.0

شرح مثال

۵. دستور continue

break از حلقه بیرون می‌پرد، ولی continue فقط سایر موارد نیزین دور را رد می‌کند و به بالای حلقه برمی‌گردد.

# 'continue' skips the REST of the current turn and goes back to the top.
# Here the odd numbers are simply ignored.
total = 0

while True:
    user_input = input('Enter a number (or "exit"): ')

    if user_input == 'exit':
        break

    number = int(user_input)

    if number % 2 != 0:
        print('Odd number ignored.')
        continue                 # go back to the input() line

    total += number
    print('Sum is:', total)

رد کردن اعداد فرد: نمونه 04_continue.py

دستوراثرش
breakخروج کامل از حلقه
continueرد کردن بقیه این دور و رفتن به دور بعد

continue بیشتر برای «فیلتر کردن» به کار می‌رود: چیزهایی که برای مسئله ما کاربردی ندارند را زود رد می‌کنیم تا کد اصلی داخل تودرتویی عمیق نیفتد. مقایسه کنید با نوشتن یک else بزرگ که همه کد اصلی را داخلش می‌برد.

۶. میانگین در حال اجرا

# Two accumulators: the total and how many numbers were entered.
total = 0
counter = 0

while True:
    user_input = input('Enter a number (or "exit"): ')

    if user_input == 'exit':
        break

    number = float(user_input)

    total += number
    counter += 1

    print('Average is:', total / counter)

# After the loop, counter may still be 0 - never divide by it blindly.
if counter == 0:
    print('No number was entered.')
else:
    print('Final average:', total / counter)

میانگین در حال اجرا: نمونه 05_running_average.py

داخل حلقه، تقسیم بر counter بی‌خطر است چون همان بالا یکی اضافه شده و حتماً بزرگ‌تر از صفر است. ولی بعد از حلقه چنین تضمینی نداریم: اگر کاربر همان اول exit بزند، شمارنده صفر می‌ماند. برای همین شرط پایانی لازم است.

۷. اعتبارسنجی ورودی

این کاربردی‌ترین الگوی این فصل است. تا وقتی کاربر جواب معتبر ندهد، سؤال را تکرار می‌کنیم.

# A very common use of 'while': keep asking until the answer makes sense.
while True:
    user_input = input('Enter your age (1-120): ')

    if not user_input.isdigit():
        print('Please type digits only.')
        continue

    age = int(user_input)

    if age < 1 or age > 120:
        print('The age must be between 1 and 120.')
        continue

    break        # the value is valid, leave the loop

print('Your age is', age)

اعتبارسنجی: نمونه 06_input_validation.py

Enter your age (1-120): abc
Please type digits only.
Enter your age (1-120): 500
The age must be between 1 and 120.
Enter your age (1-120): 25
Your age is 25

شرح مثال

۸. بازی حدس عدد

# A tiny game: the program knows a number, the player has to find it.
SECRET = 16

tries = 0

while True:
    user_input = input('Enter a number: ')
    guess = int(user_input)
    tries += 1

    if guess < SECRET:
        print('The number is greater!')
    elif guess > SECRET:
        print('The number is smaller!')
    else:
        print(f'You win! You needed {tries} tries.')
        break

حدس عدد: نمونه 07_guess_the_number.py

Enter a number: 10
The number is greater!
Enter a number: 20
The number is smaller!
Enter a number: 16
You win! You needed 3 tries.

این اولین بازی شماست. هنوز کامل نیست: عدد ثابت است و بعد از یک بار بازی، دیگر جالب نیست. در فصل شانزدهم با random عدد را تصادفی می‌کنیم و در فصل بیستم نسخه کاملش را با محدودیت تعداد حدس می‌سازیم.

۹. تشخیص عدد اول

# A prime number is only divisible by 1 and by itself.
# As soon as we find one divisor we can stop looking -> 'break'.
user_input = input('Enter your number: ')
number = int(user_input)

# 0, 1 and the negative numbers are NOT prime, so we handle them separately.
if number < 2:
    is_prime = False
else:
    is_prime = True
    for i in range(2, number):
        if number % i == 0:
            is_prime = False
            break            # no need to test the remaining divisors

if is_prime:
    print("It's prime!")
else:
    print("It's not prime.")

عدد اول: نمونه 08_is_prime.py

شرح مثال

باگی که در نسخه اصلی بود

نسخه اصلی این برنامه شرط number < 2 را نداشت. نتیجه این بود که عدد ۱ و عدد صفر و حتی اعداد منفی «اول» اعلام می‌شدند، چون حلقه range(2, 1) اصلاً اجرا نمی‌شد و پرچم True باقی می‌ماند. درس: همیشه حالت‌های مرزی را جدا بررسی کنید. حلقه‌ای که هیچ دوری نمی‌زند، خطر پنهان دارد.

۱۰. بلوک else حلقه

این یکی از ویژگی‌های عجیب و کم‌شناخته پایتون است: حلقه می‌تواند else داشته باشد. آن بلوک وقتی اجرا می‌شود که حلقه به طور طبیعی تمام شود، یعنی با break قطع نشده باشد.

# A loop may have an 'else' block. It runs only when the loop finished
# normally, that is when it was NOT stopped by a 'break'.
number = int(input('Enter a number: '))

for i in range(2, number):
    if number % i == 0:
        print(f'{number} is divisible by {i}')
        break
else:
    # reached only when no divisor was found
    print(f'{number} is prime!')

else حلقه: نمونه 09_while_else.py

مقایسه کنید با نسخه قبلی: اینجا دیگر متغیر پرچم ضرورتی ندارد. اگر حلقه مقسوم‌علیهی پیدا کند، break می‌زند و else اجرا نمی‌شود. اگر تا آخر برود و چیزی پیدا نکند، else اجرا می‌شود.

اسمش گمراه‌کننده است

بهتر بود اسمش nobreak بود، نه else. چون معنایش «اگر حلقه شکسته نشد» است، نه «در غیر این صورت». طراح پایتون هم بعدها گفته که این نام‌گذاری اشتباه بوده است. با این حال، الگوی «جست‌وجو کن و اگر پیدا نشد کاری بکن» را واقعاً تمیز می‌نویسد.

۱۱. باز کردن یک عدد رقم به رقم

# Taking a number apart digit by digit is a classic 'while' exercise.
user_input = input('Enter a positive number: ')
number = int(user_input)

digit_count = 0
digit_sum = 0

while number > 0:
    digit = number % 10       # the last digit
    digit_sum += digit
    digit_count += 1
    number = number // 10     # remove the last digit

print('Number of digits:', digit_count)
print('Sum of the digits:', digit_sum)

ارقام یک عدد: نمونه 10_digits_of_number.py

Enter a positive number: 4728
Number of digits: 4
Sum of the digits: 21

شرح مثال

عدد ۴۷۲۸ را دنبال کنیم:

دورnumbernumber % 10number // 10
۱۴۷۲۸۸۴۷۲
۲۴۷۲۲۴۷
۳۴۷۷۴
۴۴۴۰

وقتی number به صفر رسید، شرط حلقه نادرست می‌شود و کار تمام است. این همان ترکیب % و // فصل دوم است که حالا داخل یک حلقه قدرت واقعی‌اش را نشان می‌دهد. با همین الگو می‌شود عدد را برعکس کرد، که یکی از تمرین‌های این فصل است.

فایل‌های تمرینی این فصل

پوشه S08-while-loops شامل نمونه‌های 01_first_while.py تا 10_digits_of_number.py.

خلاصه فصل

ابزاروظیفه آن
while شرط:تکرار تا وقتی شرط درست است
سه جزء لازممقدار اولیه، شرط، تغییر داخل بدنه
حلقه بی‌پایانشرطی که هرگز نادرست نمی‌شود. توقف با Ctrl + C
while True: با breakپرکاربردترین الگو در برنامه‌های تعاملی
breakخروج فوری از حلقه
continueرد کردن بقیه دور جاری
نگهبانمقدار خاصی مثل exit که یعنی «تمام»
حلقه اعتبارسنجیتا وقتی ورودی معتبر نشده، دوباره بپرس
متغیر پرچممتغیر بولی که نتیجه جست‌وجو را نگه می‌دارد
else حلقهفقط وقتی حلقه بدون break تمام شود

تمرین‌های فصل هشت

  1. تا وقتی کاربر exit نزده عدد بگیرید و در پایان بزرگ‌ترین را چاپ کنید.
  2. یک پرسش رمز بنویسید که به کاربر سه فرصت بدهد.
  3. عدد بگیرید تا exit، و بگویید چند تا مثبت و چند تا منفی بودند.
  4. یک عدد بگیرید و برعکسش کنید، مثلاً 123 به 321، فقط با // و %.
  5. بازی حدس عدد را طوری بنویسید که کامپیوتر عدد را تصادفی انتخاب کند. کافی است import random کنید؛ در فصل شانزدهم کاملش را می‌بینید.
  6. یک عدد بگیرید و همه مقسوم‌علیه‌هایش را چاپ کنید.
  7. دو عدد بگیرید و بزرگ‌ترین مقسوم‌علیه مشترکشان را با یک حلقه while پیدا کنید.
  8. منوی فصل پنجم را داخل یک حلقه بگذارید تا تا وقتی کاربر گزینه صفر را نزده تکرار شود.
فصل قبل: حلقه‌های تودرتو فصل بعد: لیست‌ها