منابع ترجمه · Think Python · فصل ۵

دستورهای شرطی و بازگشت (فصل ۵ Think Python)

۲۰ دقیقه مطالعه · به‌روزرسانی ۲ آبان ۱۴۰۵

منبع اصلی: Think Python, Chapter 5: Conditionals and Recursion — Allen B. Downey

💡 این صفحه مثل یک نوت‌بوک Jupyter است: متغیر و تابعی که در یک سلول تعریف شود، در سلول‌های بعدی همین صفحه در دسترس است. سلول‌ها را به ترتیب اجرا کنید، یا با «⏮ تا اینجا» همهٔ سلول‌های قبلیِ اجرانشده را یک‌جا اجرا کنید.

دو نکته درباره‌ی این ترجمه: (۱) کتاب اصلی در دو مثال از پرسش‌هایی برگرفته از یک اسکچ طنز تلویزیونی استفاده می‌کند؛ در این ترجمه آن‌ها را با پرسش‌های ساده‌ی خودم جایگزین کرده‌ام، بدون تغییر در مفهوم مثال‌ها. (۲) خروجی‌های نمایش‌داده‌شده‌ی زیر کدها را با اجرای همین کدها به دست آورده‌ام.

موضوع اصلی این فصل دستور if است که بسته به وضعیت برنامه، کدهای متفاوتی را اجرا می‌کند. و با دستور if می‌توانیم یکی از قدرتمندترین ایده‌های علوم کامپیوتر، یعنی بازگشت (recursion)، را بررسی کنیم.

اما با سه قابلیت جدید شروع می‌کنیم: عملگر باقی‌مانده (modulus)، عبارت‌های بولی و عملگرهای منطقی.

۵.۱. تقسیم صحیح و باقی‌مانده

به یاد بیاورید که عملگر تقسیم صحیح، //، دو عدد را بر هم تقسیم می‌کند و به پایین تا یک عدد صحیح گرد می‌کند. برای مثال، فرض کنید مدت یک فیلم ۱۰۵ دقیقه است. شاید بخواهید بدانید این چند ساعت است. تقسیم معمولی یک عدد اعشاری برمی‌گرداند:

minutes = 105
minutes / 60
1.75

اما ما معمولاً ساعت‌ها را با ممیز نمی‌نویسیم. تقسیم صحیح تعداد صحیح ساعت‌ها را، با گرد کردن به پایین، برمی‌گرداند:

minutes = 105
hours = minutes // 60
hours
1

برای به دست آوردن باقی‌مانده، می‌توانید یک ساعت را (بر حسب دقیقه) کم کنید:

remainder = minutes - hours * 60
remainder
45

یا می‌توانید از عملگر باقی‌مانده (modulus operator)، %، استفاده کنید که دو عدد را بر هم تقسیم می‌کند و باقی‌مانده را برمی‌گرداند.

remainder = minutes % 60
remainder
45

عملگر باقی‌مانده از آنچه به نظر می‌رسد مفیدتر است. برای مثال، می‌تواند بررسی کند که آیا یک عدد بر عدد دیگر بخش‌پذیر است یا نه – اگر x % y صفر باشد، x بر y بخش‌پذیر است.

همچنین می‌تواند رقم یا رقم‌های سمت راست یک عدد را استخراج کند. برای مثال، x % 10 سمت‌راست‌ترین رقم x (در مبنای ۱۰) را می‌دهد. به همین ترتیب، x % 100 دو رقم آخر را می‌دهد.

x = 123
x % 10
3
x % 100
23

سرانجام، عملگر باقی‌مانده می‌تواند «حساب ساعتی» انجام دهد. برای مثال، اگر رویدادی ساعت ۱۱ صبح شروع شود و سه ساعت طول بکشد، می‌توانیم از عملگر باقی‌مانده برای فهمیدن ساعت پایان آن استفاده کنیم.

start = 11
duration = 3
end = (start + duration) % 12
end
2

این رویداد ساعت ۲ بعدازظهر تمام می‌شود.

۵.۲. عبارت‌های بولی

عبارت بولی (boolean expression) عبارتی است که یا درست است یا نادرست. برای مثال، عبارت‌های زیر از عملگر مساوی، ==، استفاده می‌کنند که دو مقدار را مقایسه می‌کند و اگر برابر باشند True و در غیر این صورت False تولید می‌کند:

5 == 5
True
5 == 7
False

یک خطای رایج استفاده از یک علامت مساوی (=) به جای دو علامت مساوی (==) است. یادتان باشد = یک مقدار را به یک متغیر نسبت می‌دهد و == دو مقدار را مقایسه می‌کند.

x = 5
y = 7
x == y
False

True و False مقدارهای خاصی هستند که به نوع bool تعلق دارند؛ رشته نیستند:

type(True)
bool
type(False)
bool

عملگر == یکی از عملگرهای رابطه‌ای (relational operators) است؛ بقیه این‌ها هستند:

x != y               # x is not equal to y
True
x > y                # x is greater than y
False
x < y               # x is less than to y
True
x >= y               # x is greater than or equal to y
False
x <= y               # x is less than or equal to y
True

۵.۳. عملگرهای منطقی

برای ترکیب مقدارهای بولی در عبارت‌ها می‌توانیم از عملگرهای منطقی (logical operators) استفاده کنیم. رایج‌ترین آن‌ها and، or و not هستند. معنای این عملگرها شبیه معنایشان در زبان انگلیسی است. برای مثال، مقدار عبارت زیر فقط وقتی True است که x بزرگ‌تر از 0 و کوچک‌تر از 10 باشد.

x > 0 and x < 10
True

عبارت زیر اگر یکی یا هر دو شرط درست باشند True است، یعنی اگر عدد بر ۲ یا ۳ بخش‌پذیر باشد:

x % 2 == 0 or x % 3 == 0
False

سرانجام، عملگر not یک عبارت بولی را نقیض می‌کند، پس عبارت زیر اگر x > y برابر False باشد True است.

not x > y
True

به‌طور دقیق، عملوندهای یک عملگر منطقی باید عبارت‌های بولی باشند، اما پایتون چندان سخت‌گیر نیست. هر عدد غیرصفر True تفسیر می‌شود:

42 and True
True

این انعطاف می‌تواند مفید باشد، اما ظرافت‌هایی دارد که ممکن است گیج‌کننده باشند. شاید بخواهید از آن دوری کنید.

۵.۴. دستورهای if

برای نوشتن برنامه‌های مفید، تقریباً همیشه به توانایی بررسی شرط‌ها و تغییر رفتار برنامه بر اساس آن‌ها نیاز داریم. دستورهای شرطی (conditional statements) این توانایی را به ما می‌دهند. ساده‌ترین شکل آن دستور if است:

if x > 0:
    print('x is positive')

if یک کلمه‌ی کلیدی پایتون است. دستورهای if ساختاری مثل تعریف تابع دارند: یک سرآیند و بعد از آن یک دستور یا دنباله‌ای از دستورها با تورفتگی که بلوک (block) نامیده می‌شود.

عبارت بولی بعد از if شرط (condition) نامیده می‌شود. اگر درست باشد، دستورهای بلوک با تورفتگی اجرا می‌شوند. اگر نه، اجرا نمی‌شوند.

محدودیتی برای تعداد دستورهایی که در بلوک می‌آیند وجود ندارد، اما حداقل باید یکی باشد. گاهی مفید است بلوکی داشته باشیم که هیچ کاری نمی‌کند – معمولاً به‌عنوان جانگهدار برای کدی که هنوز ننوشته‌اید. در آن صورت می‌توانید از دستور pass استفاده کنید که هیچ کاری نمی‌کند.

if x < 0:
    pass          # TODO: need to handle negative values!

کلمه‌ی TODO در یک کامنت یادآور قراردادی است که چیزی هست که باید بعداً انجام دهید.

۵.۵. بند else

یک دستور if می‌تواند بخش دومی هم داشته باشد که بند else نامیده می‌شود. نحو آن این‌طور است:

if x % 2 == 0:
    print('x is even')
else:
    print('x is odd')
x is odd

اگر شرط درست باشد، دستور اول با تورفتگی اجرا می‌شود؛ در غیر این صورت دستور دوم اجرا می‌شود.

در این مثال، اگر x زوج باشد، باقی‌مانده‌ی تقسیم x بر 2 برابر 0 است، پس شرط درست است و برنامه x is even را نمایش می‌دهد. اگر x فرد باشد، باقی‌مانده 1 است، پس شرط نادرست است و برنامه x is odd را نمایش می‌دهد.

چون شرط باید درست یا نادرست باشد، دقیقاً یکی از گزینه‌ها اجرا می‌شود. به این گزینه‌ها شاخه (branch) می‌گویند.

۵.۶. شرط‌های زنجیره‌ای

گاهی بیش از دو امکان وجود دارد و به بیش از دو شاخه نیاز داریم. یک راه برای بیان چنین محاسبه‌ای شرط زنجیره‌ای (chained conditional) است که شامل بند elif می‌شود.

if x < y:
    print('x is less than y')
elif x > y:
    print('x is greater than y')
else:
    print('x and y are equal')
x is less than y

elif مخفف «else if» است. محدودیتی برای تعداد بندهای elif وجود ندارد. اگر بند else وجود داشته باشد، باید در انتها باشد، اما لازم نیست حتماً وجود داشته باشد.

هر شرط به ترتیب بررسی می‌شود. اگر اولی نادرست باشد، بعدی بررسی می‌شود و همین‌طور تا آخر. اگر یکی از آن‌ها درست باشد، شاخه‌ی متناظر اجرا می‌شود و دستور if تمام می‌شود. حتی اگر بیش از یک شرط درست باشد، فقط اولین شاخه‌ی درست اجرا می‌شود.

۵.۷. شرط‌های تودرتو

یک دستور شرطی را می‌توان داخل دیگری هم گذاشت. می‌توانستیم مثال بخش قبل را این‌طور بنویسیم:

if x == y:
    print('x and y are equal')
else:
    if x < y:
        print('x is less than y')
    else:
        print('x is greater than y')
x is less than y

دستور if بیرونی دو شاخه دارد. شاخه‌ی اول یک دستور ساده دارد. شاخه‌ی دوم دستور if دیگری دارد که خودش دو شاخه دارد. هر دو آن شاخه‌ها دستورهای ساده‌اند، هرچند می‌توانستند دستورهای شرطی هم باشند.

هرچند تورفتگی دستورها ساختار را آشکار می‌کند، شرط‌های تودرتو (nested conditionals) می‌توانند خواندنشان سخت باشد. پیشنهاد می‌کنم هر وقت می‌توانید از آن‌ها دوری کنید.

عملگرهای منطقی اغلب راهی برای ساده کردن دستورهای شرطی تودرتو فراهم می‌کنند. این مثالی با یک شرط تودرتوست.

if 0 < x:
    if x < 10:
        print('x is a positive single-digit number.')

دستور print فقط وقتی اجرا می‌شود که از هر دو شرط بگذریم، پس با عملگر and همان اثر را به دست می‌آوریم.

if 0 < x and x < 10:
    print('x is a positive single-digit number.')

برای این نوع شرط، پایتون گزینه‌ی مختصرتری فراهم می‌کند:

if 0 < x < 10:
    print('x is a positive single-digit number.')

(با x = 5 هر سه نسخه‌ی بالا عبارت x is a positive single-digit number. را چاپ می‌کنند.)

۵.۸. بازگشت

برای یک تابع مجاز است خودش را فراخوانی کند. شاید روشن نباشد که چرا این چیز خوبی است، اما معلوم می‌شود از جادویی‌ترین کارهایی است که یک برنامه می‌تواند بکند. این یک مثال است.

def countdown(n):
    if n <= 0:
        print('Blastoff!')
    else:
        print(n)
        countdown(n-1)

اگر n برابر ۰ یا منفی باشد، countdown کلمه‌ی «Blastoff!» را چاپ می‌کند. در غیر این صورت n را چاپ می‌کند و بعد خودش را فراخوانی می‌کند و n-1 را به‌عنوان آرگومان می‌دهد.

این اتفاقی است که وقتی این تابع را با آرگومان 3 فراخوانی می‌کنیم می‌افتد.

countdown(3)
3
2
1
Blastoff!

اجرای countdown با n=3 شروع می‌شود، و چون n بزرگ‌تر از 0 است، 3 را نمایش می‌دهد و بعد خودش را فراخوانی می‌کند…

اجرای countdown با n=2 شروع می‌شود، و چون n بزرگ‌تر از 0 است، 2 را نمایش می‌دهد و بعد خودش را فراخوانی می‌کند…

اجرای countdown با n=1 شروع می‌شود، و چون n بزرگ‌تر از 0 است، 1 را نمایش می‌دهد و بعد خودش را فراخوانی می‌کند…

اجرای countdown با n=0 شروع می‌شود، و چون n بزرگ‌تر از 0 نیست، «Blastoff!» را نمایش می‌دهد و برمی‌گردد.

countdownای که n=1 گرفته بود برمی‌گردد.

countdownای که n=2 گرفته بود برمی‌گردد.

countdownای که n=3 گرفته بود برمی‌گردد.

تابعی که خودش را فراخوانی می‌کند بازگشتی (recursive) است. به‌عنوان مثالی دیگر، می‌توانیم تابعی بنویسیم که یک رشته را n بار چاپ می‌کند.

def print_n_times(string, n):
    if n > 0:
        print(string)
        print_n_times(string, n-1)

اگر n مثبت باشد، print_n_times مقدار string را نمایش می‌دهد و بعد خودش را فراخوانی می‌کند و string و n-1 را به‌عنوان آرگومان می‌دهد.

اگر n برابر 0 یا منفی باشد، شرط نادرست است و print_n_times هیچ کاری نمی‌کند.

این‌طور کار می‌کند.

print_n_times('Spam ', 4)
Spam 
Spam 
Spam 
Spam 

برای مثال‌های ساده‌ای مثل این، احتمالاً استفاده از حلقه‌ی for آسان‌تر است. اما بعداً مثال‌هایی می‌بینیم که نوشتنشان با حلقه‌ی for سخت و با بازگشت آسان است، پس خوب است زود شروع کنیم.

۵.۹. نمودارهای پشته برای توابع بازگشتی

این یک نمودار پشته است که قاب‌های ساخته‌شده هنگام فراخوانی countdown با n = 3 را نشان می‌دهد.

(در کتاب اصلی این‌جا نمودار رسم شده است: چهار قاب countdown روی هم، با n برابر ۳، ۲، ۱ و ۰.)

چهار قاب countdown برای پارامتر n مقدارهای متفاوتی دارند. ته پشته، جایی که n=0 است، حالت پایه (base case) نامیده می‌شود. فراخوانی بازگشتی انجام نمی‌دهد، پس قاب دیگری وجود ندارد.

(نمودار پشته‌ی print_n_times هم در کتاب آمده است: سه قاب با string='Hello' و n برابر ۲، ۱ و ۰.)

۵.۱۰. بازگشت بی‌نهایت

اگر یک بازگشت هرگز به حالت پایه نرسد، برای همیشه فراخوانی بازگشتی انجام می‌دهد و برنامه هرگز پایان نمی‌یابد. به این بازگشت بی‌نهایت (infinite recursion) می‌گویند و معمولاً ایده‌ی خوبی نیست. این یک تابع حداقلی با بازگشت بی‌نهایت است.

def recurse():
    recurse()

هر بار که recurse فراخوانی می‌شود، خودش را فراخوانی می‌کند که قاب دیگری می‌سازد. در پایتون محدودیتی برای تعداد قاب‌هایی که می‌توانند هم‌زمان روی پشته باشند وجود دارد. اگر برنامه از این محدودیت فراتر برود، خطای زمان اجرا ایجاد می‌شود.

recurse()
RecursionError: maximum recursion depth exceeded

ردیابی (traceback) نشان می‌دهد هنگام رخ دادن خطا حدود ۳۰۰۰ قاب روی پشته بوده است.

اگر ناخواسته با یک بازگشت بی‌نهایت مواجه شدید، تابع‌تان را مرور کنید تا مطمئن شوید حالت پایه‌ای هست که فراخوانی بازگشتی انجام نمی‌دهد. و اگر حالت پایه هست، بررسی کنید که آیا رسیدن به آن تضمین شده است یا نه.

۵.۱۱. ورودی از صفحه‌کلید

برنامه‌هایی که تا اینجا نوشته‌ایم هیچ ورودی‌ای از کاربر نمی‌پذیرند. هر بار فقط همان کار را می‌کنند.

پایتون تابع داخلی‌ای به نام input دارد که برنامه را متوقف می‌کند و منتظر می‌ماند کاربر چیزی تایپ کند. وقتی کاربر Return یا Enter را می‌زند، برنامه ادامه پیدا می‌کند و input آنچه را کاربر تایپ کرده به‌صورت رشته برمی‌گرداند.

text = input()

قبل از گرفتن ورودی از کاربر، شاید بخواهید یک اعلان (prompt) نمایش دهید که به کاربر بگوید چه چیزی تایپ کند. input می‌تواند یک اعلان را به‌عنوان آرگومان بگیرد:

name = input('What is your name?\n')
name

(اگر کاربر مثلاً Arthur را تایپ کند، name برابر 'Arthur' می‌شود.)

دنباله‌ی \n در انتهای اعلان یک خط جدید (newline) را نشان می‌دهد که نویسه‌ی خاصی است و باعث شکستن خط می‌شود – به این ترتیب ورودی کاربر زیر اعلان ظاهر می‌شود.

اگر انتظار دارید کاربر یک عدد صحیح تایپ کند، می‌توانید از تابع int برای تبدیل مقدار بازگشتی به int استفاده کنید.

prompt = 'How old are you?\n'
age = input(prompt)
age

(اگر کاربر مثلاً about forty را تایپ کند، age برابر 'about forty' می‌شود.)

اما اگر چیزی تایپ کنند که عدد صحیح نیست، خطای زمان اجرا می‌گیرید.

int(age)
ValueError: invalid literal for int() with base 10: 'about forty'

بعداً می‌بینیم چگونه این نوع خطا را مدیریت کنیم.

۵.۱۲. اشکال‌زدایی

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

  • خطا از چه نوعی بود، و

  • کجا رخ داد.

خطاهای نحوی معمولاً پیدا کردنشان آسان است، اما چند دام وجود دارد. خطاهای مربوط به فاصله‌ها و تب‌ها می‌توانند حیله‌گر باشند، چون دیده نمی‌شوند و ما عادت داریم نادیده‌شان بگیریم.

x = 5
 y = 6
IndentationError: unexpected indent

در این مثال، مشکل این است که خط دوم با یک فاصله تورفتگی دارد. اما پیام خطا به y اشاره می‌کند که گمراه‌کننده است. پیام‌های خطا نشان می‌دهند مشکل کجا کشف شده، اما خطای واقعی ممکن است زودتر در کد باشد.

درباره‌ی خطاهای زمان اجرا هم همین‌طور است. برای مثال، فرض کنید می‌خواهید یک نسبت را به دسی‌بل تبدیل کنید، مثل این:

import math
numerator = 9
denominator = 10
ratio = numerator // denominator
decibels = 10 * math.log10(ratio)
ValueError: math domain error

پیام خطا به خط ۵ اشاره می‌کند، اما هیچ مشکلی در آن خط نیست. مشکل در خط ۴ است که به جای تقسیم اعشاری از تقسیم صحیح استفاده کرده است – در نتیجه مقدار ratio برابر 0 می‌شود. وقتی math.log10 را فراخوانی می‌کنیم، ValueError با پیام math domain error می‌گیریم، چون 0 در «دامنه»ی آرگومان‌های معتبر math.log10 نیست، زیرا لگاریتم 0 تعریف نشده است.

به‌طور کلی، باید وقت بگذارید و پیام‌های خطا را با دقت بخوانید، اما فرض نکنید هر آنچه می‌گویند درست است.

۵.۱۳. واژه‌نامه

بازگشت (recursion): فرایند فراخوانی تابعی که در حال حاضر در حال اجراست.

عملگر باقی‌مانده (modulus operator): عملگری، %، که روی اعداد صحیح کار می‌کند و باقی‌مانده‌ی تقسیم یک عدد بر عدد دیگر را برمی‌گرداند.

عبارت بولی (boolean expression): عبارتی که مقدارش True یا False است.

عملگر رابطه‌ای (relational operator): یکی از عملگرهایی که عملوندهایش را مقایسه می‌کند: ==، !=، >، <، >= و <=.

عملگر منطقی (logical operator): یکی از عملگرهایی که عبارت‌های بولی را ترکیب می‌کند، از جمله and، or و not.

دستور شرطی (conditional statement): دستوری که جریان اجرا را بسته به یک شرط کنترل می‌کند.

شرط (condition): عبارت بولی در یک دستور شرطی که تعیین می‌کند کدام شاخه اجرا شود.

بلوک (block): یک یا چند دستور با تورفتگی که نشان می‌دهد بخشی از دستور دیگری هستند.

شاخه (branch): یکی از دنباله‌های جایگزین دستورها در یک دستور شرطی.

شرط زنجیره‌ای (chained conditional): دستور شرطی‌ای با مجموعه‌ای از شاخه‌های جایگزین.

شرط تودرتو (nested conditional): دستور شرطی‌ای که در یکی از شاخه‌های دستور شرطی دیگری می‌آید.

بازگشتی (recursive): تابعی که خودش را فراخوانی می‌کند بازگشتی است.

حالت پایه (base case): شاخه‌ای شرطی در یک تابع بازگشتی که فراخوانی بازگشتی انجام نمی‌دهد.

بازگشت بی‌نهایت (infinite recursion): بازگشتی که حالت پایه ندارد یا هرگز به آن نمی‌رسد. بازگشت بی‌نهایت در نهایت باعث خطای زمان اجرا می‌شود.

خط جدید (newline): نویسه‌ای که بین دو بخش یک رشته شکست خط ایجاد می‌کند.

۵.۱۴. تمرین‌ها

# This cell tells Jupyter to provide detailed debugging information
# when a runtime error occurs. Run it before working on the exercises.

%xmode Verbose

۵.۱۴.۱. از یک دستیار مجازی بپرسید

  • از یک دستیار مجازی بپرسید «چند کاربرد عملگر باقی‌مانده چیست؟» (What are some uses of the modulus operator?)

  • پایتون عملگرهایی برای محاسبه‌ی عملیات منطقی and، or و not دارد، اما عملگری برای محاسبه‌ی «یای انحصاری» که معمولاً xor نوشته می‌شود ندارد. از یک دستیار بپرسید «عمل منطقی xor چیست و چطور در پایتون محاسبه‌اش کنم؟» (What is the logical xor operation and how do I compute it in Python?)

در این فصل دو راه برای نوشتن یک دستور if با سه شاخه دیدیم: شرط زنجیره‌ای یا شرط تودرتو. می‌توانید از یک دستیار مجازی برای تبدیل یکی به دیگری استفاده کنید. برای مثال، از دستیار بخواهید «این دستور را به یک شرط زنجیره‌ای تبدیل کن.» (Convert this statement to a chained conditional.)

x = 5
y = 7
if x == y:
    print('x and y are equal')
else:
    if x < y:
        print('x is less than y')
    else:
        print('x is greater than y')

از دستیار بخواهید «این دستور را با یک شرط واحد بازنویسی کن.» (Rewrite this statement with a single conditional.)

if 0 < x:
    if x < 10:
        print('x is a positive single-digit number.')

ببینید دستیار می‌تواند این پیچیدگی غیرضروری را ساده کند.

if not x <= 0 and not x >= 10:
    print('x is a positive single-digit number.')

این تلاشی برای نوشتن یک تابع بازگشتی است که دو تا دو تا به عقب می‌شمارد.

def countdown_by_two(n):
    if n == 0:
        print('Blastoff!')
    else:
        print(n)
        countdown_by_two(n-2)

به نظر می‌رسد کار می‌کند.

countdown_by_two(6)
6
4
2
Blastoff!

اما خطا دارد. از یک دستیار مجازی بپرسید چه ایرادی دارد و چطور درستش کنیم. راه‌حلی را که می‌دهد این‌جا پیست کنید و آزمایشش کنید.

۵.۱۴.۲. تمرین

ماژول time تابعی دارد که آن هم time نام دارد و تعداد ثانیه‌های گذشته از «آغاز یونیکس» (Unix epoch) را برمی‌گرداند، یعنی ۱ ژانویه‌ی ۱۹۷۰ ساعت ۰۰:۰۰:۰۰ به وقت UTC (زمان هماهنگ جهانی).

from time import time

now = time()
now

با استفاده از تقسیم صحیح و عملگر باقی‌مانده، تعداد روزهای گذشته از ۱ ژانویه‌ی ۱۹۷۰ و ساعت فعلی روز را بر حسب ساعت، دقیقه و ثانیه حساب کنید.

درباره‌ی ماژول time می‌توانید بیشتر در https://docs.python.org/3/library/time.html بخوانید.

# Solution goes here
# Solution goes here
# Solution goes here
# Solution goes here

۵.۱۴.۳. تمرین

اگر سه چوب به شما بدهند، ممکن است بتوانید با آن‌ها مثلث بسازید یا نتوانید. برای مثال، اگر طول یکی از چوب‌ها ۱۲ اینچ باشد و دو تای دیگر یک اینچ، نمی‌توانید کاری کنید که چوب‌های کوتاه در وسط به هم برسند. برای هر سه طولی، آزمونی وجود دارد که نشان می‌دهد تشکیل مثلث ممکن است یا نه:

اگر هر یک از سه طول بزرگ‌تر از مجموع دو طول دیگر باشد، نمی‌توانید مثلث بسازید. در غیر این صورت می‌توانید. (اگر مجموع دو طول برابر طول سوم باشد، آنچه «مثلث تباهیده» نامیده می‌شود پدید می‌آید.)

تابعی به نام is_triangle بنویسید که سه عدد صحیح را به‌عنوان آرگومان بگیرد و بسته به اینکه بتوان با چوب‌هایی با این طول‌ها مثلث ساخت یا نه، «Yes» یا «No» چاپ کند. راهنمایی: از یک شرط زنجیره‌ای استفاده کنید.

# Solution goes here

تابع‌تان را با موارد زیر آزمایش کنید.

is_triangle(4, 5, 6)   # should be Yes
is_triangle(1, 2, 3)   # should be Yes
is_triangle(6, 2, 3)   # should be No
is_triangle(1, 1, 12)   # should be No

۵.۱۴.۴. تمرین

خروجی برنامه‌ی زیر چیست؟ یک نمودار پشته رسم کنید که وضعیت برنامه را هنگام چاپ نتیجه نشان دهد.

def recurse(n, s):
    if n == 0:
        print(s)
    else:
        recurse(n-1, n+s)

recurse(3, 0)
# Solution goes here
# Solution goes here

۵.۱۴.۵. تمرین

تمرین‌های زیر از ماژول jupyturtle که در فصل ۴ توضیح داده شد استفاده می‌کنند.

تابع زیر را بخوانید و ببینید می‌توانید بفهمید چه می‌کند. بعد اجرایش کنید و ببینید درست حدس زده‌اید یا نه. مقدارهای length، angle و factor را تغییر دهید و ببینید چه اثری روی نتیجه دارند. اگر مطمئن نیستید که فهمیده‌اید چطور کار می‌کند، از یک دستیار مجازی بپرسید.

from jupyturtle import forward, left, right, back

def draw(length):
    angle = 50
    factor = 0.6
    
    if length > 5:
        forward(length)
        left(angle)
        draw(factor * length)
        right(2 * angle)
        draw(factor * length)
        left(angle)
        back(length)
# Solution goes here

۵.۱۴.۶. تمرین

از یک دستیار مجازی بپرسید «منحنی کخ (Koch curve) چیست؟» (What is the Koch curve?)

برای کشیدن یک منحنی کخ به طول x، فقط باید این کارها را انجام دهید:

  1. یک منحنی کخ به طول x/3 بکشید.

  2. ۶۰ درجه به چپ بچرخید.

  3. یک منحنی کخ به طول x/3 بکشید.

  4. ۱۲۰ درجه به راست بچرخید.

  5. یک منحنی کخ به طول x/3 بکشید.

  6. ۶۰ درجه به چپ بچرخید.

  7. یک منحنی کخ به طول x/3 بکشید.

استثنا وقتی است که x کوچک‌تر از 5 باشد – در آن صورت می‌توانید فقط یک خط راست به طول x بکشید.

تابعی به نام koch بنویسید که x را به‌عنوان آرگومان بگیرد و یک منحنی کخ با طول داده‌شده بکشد.

# Solution goes here

نتیجه باید این‌طور به نظر برسد:

make_turtle(delay=0)
koch(120)

وقتی koch کار کرد، می‌توانید از این حلقه برای کشیدن سه منحنی کخ به شکل دانه‌ی برف استفاده کنید.

make_turtle(delay=0, height=300)
for i in range(3):
    koch(120)
    right(120)

۵.۱۴.۷. تمرین

دستیارهای مجازی توابع ماژول jupyturtle را می‌شناسند، اما نسخه‌های زیادی از این توابع با نام‌های متفاوت وجود دارد، پس ممکن است دستیار نداند درباره‌ی کدام یکی صحبت می‌کنید.

برای حل این مشکل، می‌توانید قبل از پرسیدن سؤال اطلاعات بیشتری بدهید. برای مثال، می‌توانید پرامپت را با «این برنامه‌ای است که از ماژول jupyturtle استفاده می‌کند» (Here’s a program that uses the jupyturtle module) شروع کنید و بعد یکی از مثال‌های این فصل را پیست کنید. بعد از آن، دستیار باید بتواند کدی تولید کند که از این ماژول استفاده می‌کند.

برای نمونه، از یک دستیار برنامه‌ای بخواهید که مثلث سی‌یرپینسکی (Sierpiński) را بکشد. کدی که می‌گیرید باید نقطه‌ی شروع خوبی باشد، اما شاید لازم باشد مقداری اشکال‌زدایی کنید. اگر تلاش اول جواب نداد، می‌توانید به دستیار بگویید چه اتفاقی افتاد و از او کمک بخواهید – یا خودتان اشکال‌زدایی کنید.

# Solution goes here

نتیجه ممکن است این‌طور به نظر برسد، هرچند نسخه‌ای که شما می‌گیرید ممکن است فرق کند.

make_turtle(delay=0, height=200)

draw_sierpinski(100, 3)