5. اصول برنامهنویسی پایتون#
5.1. مقدمه#
ما تاکنون مطالب زیادی را با تمرکز بر مثالها، بهسرعت پوشش دادهایم.
اکنون بیایید برخی ویژگیهای اصلی پایتون را بهصورت نظاممندتر بررسی کنیم.
این روش شاید کمتر هیجانانگیز باشد، اما به درک بهتر جزئیات کمک میکند.
5.2. انواع داده#
برنامههای کامپیوتری مجموعهای از انواع داده را مدیریت میکنند.
برای مثال، 1.5 یک عدد اعشاری (float) است، در حالی که 1 یک عدد صحیح (integer) محسوب میشود. در برنامه نویسی باید بین این دو نوع تفاوت قائل شوید، زیرا در حافظه به شکل متفاوتی ذخیره میشوند و عملیات ریاضی روی آنها فرق دارد.
به عنوان نمونه، در بیشتر رایانهها محاسبات اعشاری توسط یک واحد خاص به نام واحد ممیز شناور (FPU) انجام میشود.
به طور کلی، اعداد اعشاری اطلاعات بیشتری دارند اما عملیات ریاضی روی اعداد صحیح سریعتر و دقیقتر است.
پایتون چندین نوع دادهٔ داخلی دیگر نیز دارد، مثل رشتهها (strings) و فهرستها (lists).
بیایید بیشتر با آنها آشنا شویم.
5.2.1. انواع داده اولیه (Primitive Data Types)#
5.2.1.1. مقادیر بولی (Boolean)#
یک نوع از داده های اولیه، مقادیر بولی هستند که میتوانند فقط True (درست) یا False (نادرست) باشند.
x = True
x
True
میتوانیم نوع هر شیء در حافظه را با استفاده از تابع ()type بررسی کنیم:
type(x)
bool
مثلاً در مثال زیر، پایتون مقدار عبارت سمت راست را محاسبه کرده و آن را به متغیر y نسبت میدهد:
y = 100 < 10
y
False
type(y)
bool
در عبارات ریاضی، True معادل 1 و False معادل 0 در نظر گرفته میشود.
این ویژگی را محاسبات بولی (Boolean arithmetic) مینامند و اغلب در برنامه نویسی کاربردی است.
مثال های زیر نمونه هایی از این دست هستند:
x + y
1
x * y
0
True + True
2
bools = [True, True, False, True] # List of Boolean values
sum(bools)
3
5.2.1.2. انواع دادههای عددی#
داده های عددی از دیگر دادههای اولیه هستند.
همانطور که پیشتر با داده های صحیح (integer) و اعشاری (float) یا به اختصار intو float آشنا شدید؛ نوعی دیگر از داده های عددی اعداد مختلط (complex) نام دارند.
همانند مثال زیر:
x = complex(1, 2)
y = complex(2, 1)
print(x * y)
type(x)
5j
complex
5.2.2. ظرف ها یا ساختارهای نگه دارنده(Containers)#
پایتون چندین نوع پایه برای ذخیره سازی مجموعه هایی از داده ها(احتمالا ناهمگن) دارد.
پییشتر با لیستها آشنا شدیم.
نوعی دیگر از آن، تاپل (tuple) است که «غیرقابل تغییر» (immutable) می باشد.
x = ('a', 'b') # Parentheses instead of the square brackets
x = 'a', 'b' # Or no brackets --- the meaning is identical
x
('a', 'b')
type(x)
tuple
اگر شیئی پس از ساخته شدن قابل تغییر نباشد، immutable است. در مقابل، اگر قابل ویرایش باشد، mutable نام دارد.
در پایتون لیست ها قابل تغییر هستند:
x = [1, 2]
x[0] = 10
x
[10, 2]
اما tupleها قابل تغییر نیستند:
x = (1, 2)
x[0] = 10
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[13], line 2
1 x = (1, 2)
----> 2 x[0] = 10
TypeError: 'tuple' object does not support item assignment
کمی بعدتر درباره نقش دادههای قابل تغییر و تغییرناپذیر بیشتر صحبت خواهیم کرد.
میتوان تاپلها (و لیستها) را به صورت زیر «بازکرد» (unpack) کرد:
integers = (10, 20, 30)
x, y, z = integers
x
10
y
20
در واقع شما قبلاً نمونهای از این را دیدهاید.
باز کردن tuple راحت است و ما اغلب از آن استفاده خواهیم کرد.
5.2.2.1. نشانه گذاری برش (Slice Notation)#
برای دسترسی به چندین عنصر از یک دنباله (یک لیست، یک tuple یا یک رشته)، میتوانید از نشانهگذاری slice در پایتون استفاده کنید.
برای مثال:
a = ["a", "b", "c", "d", "e"]
a[1:]
['b', 'c', 'd', 'e']
a[1:3]
['b', 'c']
قانون کلی این است که a[m:n] تعداد n - m عنصر را بازمیگرداند، که از a[m] شروع میشود.
اعداد منفی نیز مجاز هستند:
a[-2:] # Last two elements of the list
['d', 'e']
همچنین میتوانید از قالب [start:end:step] برای مشخص کردن گام برش استفاده کنید:
a[::2]
['a', 'c', 'e']
با استفاده از یک گام منفی، میتوانید دنباله را به صورت معکوس برگردانید:
a[-2::-1] # Walk backwards from the second last element to the first element
['d', 'c', 'b', 'a']
همان نشانهگذاری slice روی tupleها و رشتهها نیز کار میکند:
s = 'foobar'
s[-3:] # Select the last three elements
'bar'
5.2.2.2. مجموعهها و دیکشنریها (Sets and Dictionaries)#
دو نوع دیگر از ظرف ها در پایتون،مجموعه ها (sets) و دیکشنری ها (dictionaries) هستند.
دیکشنریها بسیار شبیه لیستها هستند، با این تفاوت که آیتمها به جای شمارهگذاری، نامگذاری میشوند:
d = {'name': 'Frodo', 'age': 33}
type(d)
dict
d['age']
33
در این مثال از دیکشنری، نام های 'name' و'age' کلیدها(keys) نامیده میشوند.
اشیایی که کلیدها به آنها ارجاع میدهند ('Frodo' و 33) مقادیر(Values) نامیده میشوند.
مجموعهها(sets) دسته های بدون ترتیب و بدون عنصر تکراری هستند و متدهای مجموعه،عملیات معمول مربوط به مجموعه ها را فراهم می کنند:
s1 = {'a', 'b'}
type(s1)
set
s2 = {'b', 'c'}
s1.issubset(s2)
False
s1.intersection(s2)
{'b'}
تابع ()set مجموعه ها را از دنباله ها می سازد:
s3 = set(('foo', 'bar', 'foo'))
s3
{'bar', 'foo'}
5.3. ورودی و خروجی (Input and Output)#
بیایید به طور خلاصه خواندن و نوشتن در فایلهای متنی را مرور کنیم.
با نوشتن شروع میکنیم:
f = open('newfile.txt', 'w') # Open 'newfile.txt' for writing
f.write('Testing\n') # Here '\n' means new line
f.write('Testing again')
f.close()
در اینجا:
تابع سازنده داخلی
()openیک فایل برای نوشتن ایجاد می کند.هم
()writeو هم()closeمتدهای فایل های ایجادشده هستند.
این فایلی که ساختهایم کجاست؟
به خاطر داشته باشید که پایتون مفهوم دایرکتوری فعلی (present working directory یا pwd) شما را حفظ می کند، که در Jupyter یا IPython می توان آن را از طریق دستور زیر پیدا کرد:
%pwd
'/home/runner/work/lecture-python-programming.fa/lecture-python-programming.fa/lectures'
اگر مسیری مشخص نشده باشد، پایتون در همین مکان(پوشه جاری)فایل را می نویسد.
ما همچنین میتوانیم با پایتون محتوای فایل newline.txt را به صورت زیر بخوانیم:
f = open('newfile.txt', 'r')
out = f.read()
out
'Testing\nTesting again'
print(out)
Testing
Testing again
در واقع، رویکرد پیشنهادی در پایتون مدرن، استفاده از یک دستور with است تا اطمینان حاصل شود که فایلها به درستی دریافت و منتشر میشوند.
محدود کردن عملیات در همان بلوک نیز وضوح کد شما را بهبود میبخشد.
Note
این نوع بلوک به طور رسمی به عنوان یک context شناخته میشود.
بیایید سعی کنیم دو مثال بالا را به عبارت with تبدیل کنیم.
ابتدا مثال نوشتن را تغییر میدهیم:
with open('newfile.txt', 'w') as f:
f.write('Testing\n')
f.write('Testing again')
توجه کنید که نیازی به فراخوانی متد ()close نداریم زیرا بلوک with تضمین میکند که جریان(stream) در پایان بلوک بسته شود.
با کمی تغییر، میتوانیم فایلها را با استفاده از دستور with نیز بخوانیم:
with open('newfile.txt', 'r') as fo:
out = fo.read()
print(out)
Testing
Testing again
اکنون فرض کنید که میخواهیم ورودی را از یک فایل بخوانیم و خروجی را در فایل دیگری بنویسیم.
در اینجا نحوه انجام این کار در حالی که به درستی منابع را دریافت و به سیستم عامل بازمیگردانیم با استفاده از عبارات with آمده است:
with open("newfile.txt", "r") as f:
file = f.readlines()
with open("output.txt", "w") as fo:
for i, line in enumerate(file):
fo.write(f'Line {i}: {line} \n')
فایل خروجی به صورت زیر خواهد بود:
with open('output.txt', 'r') as fo:
print(fo.read())
Line 0: Testing
Line 1: Testing again
میتوانیم مثال بالا را ساده کنیم با این کار که دو دستور with را در یک خط قرار دهیم:
with open("newfile.txt", "r") as f, open("output2.txt", "w") as fo:
for i, line in enumerate(f):
fo.write(f'Line {i}: {line} \n')
فایل خروجی یکسان خواهد بود:
with open('output2.txt', 'r') as fo:
print(fo.read())
Line 0: Testing
Line 1: Testing again
فرض کنید میخواهیم به جای بازنویسی فایل موجود به نوشتن در آن ادامه دهیم.
میتوانیم حالت را به a تغییر دهیم که مخفف حالت append است:
with open('output2.txt', 'a') as fo:
fo.write('\nThis is the end of the file')
with open('output2.txt', 'r') as fo:
print(fo.read())
Line 0: Testing
Line 1: Testing again
This is the end of the file
Note
توجه کنید که ما فقط حالتهای r، w و a را در اینجا پوشش دادیم که رایجترین حالتهای مورد استفاده هستند.
پایتون حالتهای متنوعی ارائه میدهد که میتوانید با آنها آزمایش کنید.
5.3.1. مسیرها (Paths)#
توجه کنید که اگر newfile.txt در دایرکتوری کاری فعلی نباشد، فراخوانی ()open با خطا مواجه میشود.
در این حالت، میتوانید فایل را به pwd منتقل کنید یا مسیر کامل فایل را مشخص کنید:
f = open('insert_full_path_to_file/newfile.txt', 'r')
5.4. تکرار(Iteration)#
یکی از مهمترین وظایف در علوم کامپیوتر، عبور گامبهگام از یک دنباله از دادهها و انجام یک عمل مشخص است.
یکی از نقاط قوت پایتون رابط کاربری ساده و انعطافپذیر آن برای این نوع تکرار از طریق حلقه for است.
5.4.1. حلقه تکرار روی اشیاء مختلف#
بسیاری از اشیاء پایتون “قابل تکرار” هستند، به این معنی که میتوان روی آنها حلقه(loop) زد.
برای ارائه یک مثال، بیایید فایل us_cities.txt را بنویسیم که شهرهای ایالات متحده و جمعیت آنها را فهرست میکندو در پوشه کاری فعلی ذخیره می گردد:
%%writefile us_cities.txt
new york: 8244910
los angeles: 3819702
chicago: 2707120
houston: 2145146
philadelphia: 1536471
phoenix: 1469471
san antonio: 1359758
san diego: 1326179
dallas: 1223229
Overwriting us_cities.txt
در اینجا writefile%% یک IPython cell magic است.
فرض کنید میخواهیم اطلاعات را با بزرگ کردن نامها و اضافه کردن ویرگول برای نشان دادن هزارگان خواناتر کنیم.
برنامه زیر دادهها را میخواند و تبدیل را انجام میدهد:
data_file = open('us_cities.txt', 'r')
for line in data_file:
city, population = line.split(':') # Tuple unpacking
city = city.title() # Capitalize city names
population = f'{int(population):,}' # Add commas to numbers
print(city.ljust(15) + population)
data_file.close()
New York 8,244,910
Los Angeles 3,819,702
Chicago 2,707,120
Houston 2,145,146
Philadelphia 1,536,471
Phoenix 1,469,471
San Antonio 1,359,758
San Diego 1,326,179
Dallas 1,223,229
در اینجا 'f یک f-string است که برای درج متغیرها در رشتهها استفاده میشود.
قالببندی مجدد هر خط نتیجه سه متد مختلف رشته است که بررسی جزئیات آنها را میتوان برای بعد گذاشت.
بخش جالب این برنامه برای ما خط 2 است که نشان میدهد:
شیء فایل
data_fileقابل تکرار است، به این معنی که میتوان آن را در سمت راستinدر یک حلقهforقرار داد.تکرار به صورت خط به خط از فایل عبور میکند.
این همان چیزی است که منجر به سینتکس ساده و کاربردی نمایش داده شده در برنامه ما می شود.
بسیاری از انواع دیگر اشیاء قابل تکرار هستند و ما بعداً درباره برخی از آنها بحث خواهیم کرد.
5.4.2. حلقه تکرار بدون ایندکسها#
یکی از چیزهایی که ممکن است متوجه شده باشید این است که پایتون تمایل دارد مستقیما روی آیتم ها حلقه بزنیم، بدون اینکه شماره موقعیت(یا همان ایندکس) هر آیتم را به صورت صریح استفاده کنیم.
برای مثال:
x_values = [1, 2, 3] # Some iterable x
for x in x_values:
print(x * x)
1
4
9
به این ترجیح داده میشود:
for i in range(len(x_values)):
print(x_values[i] * x_values[i])
1
4
9
وقتی این دو گزینه را مقایسه میکنید، میتوانید ببینید چرا اولی ترجیح داده میشود.
پایتون امکاناتی دارد که کار با حلقه ها را بدون ایندکس ساده میکند.
یکی از این امکانات ()zip است که به شما اجازه میدهد دو لیست را هم زمان با هم طی کنید و هر بار یک جفت از عناصر آنها را بگیرید.
برای مثال، کد زیر را اجرا کنید:
countries = ('Japan', 'Korea', 'China')
cities = ('Tokyo', 'Seoul', 'Beijing')
for country, city in zip(countries, cities):
print(f'The capital of {country} is {city}')
The capital of Japan is Tokyo
The capital of Korea is Seoul
The capital of China is Beijing
تابع ()zip همچنین برای ایجاد دیکشنریها کاربردی است، برای مثال:
names = ['Tom', 'John']
marks = ['E', 'F']
dict(zip(names, marks))
{'Tom': 'E', 'John': 'F'}
اگر واقعاً به شماره موقعیت یا ایندکس از یک لیست نیاز داریم، یکی از گزینهها استفاده از ()enumerate است.
برای درک اینکه ()enumerate چه کاری انجام میدهد، مثال زیر را در نظر بگیرید:
letter_list = ['a', 'b', 'c']
for index, letter in enumerate(letter_list):
print(f"letter_list[{index}] = '{letter}'")
letter_list[0] = 'a'
letter_list[1] = 'b'
letter_list[2] = 'c'
5.4.3. خلاصه لیست (List Comprehension)#
همچنین میتوانیم کد تولید لیست مقادیر تصادفی را با استفاده از چیزی به نام خلاصه لیست(list comprehension) به طور قابل توجهی ساده کنیم.
خلاصه لیستها یک ابزار زیبای پایتون برای ایجاد لیست هستند.
مثال زیر را در نظر بگیرید، که خلاصه لیست در سمت راست خط دوم است:
animals = ['dog', 'cat', 'bird']
plurals = [animal + 's' for animal in animals]
plurals
['dogs', 'cats', 'birds']
این یک مثال دیگر است:
range(8)
range(0, 8)
doubles = [2 * x for x in range(8)]
doubles
[0, 2, 4, 6, 8, 10, 12, 14]
5.5. عملگرهای مقایسهای و منطقی#
5.5.1. عملگرهای مقایسهای#
بسیاری از انواع مختلف عبارات وجود دارند که نتیجهشان با یکی از مقادیر Boolean (یعنی True یا False) نشان داده میشوند.
یکی از رایج ترین این عبارت ها، عملگرهای مقایسهای هستند، مانند:
x, y = 1, 2
x < y
True
x > y
False
یکی از ویژگیهای خوب پایتون این است که میتوان نامساویها را به صورت پشت سر هم (Chain) در یک عبارت نوشت.
1 < 2 < 3
True
1 <= 2 <= 3
True
همانطور که قبلاً دیدیم، هنگام آزمایش برابری از == استفاده میکنیم:
x = 1 # Assignment
x == 2 # Comparison
False
برای “نابرابر” از != استفاده کنید:
1 != 2
True
توجه کنید که هنگام آزمایش شرایط، میتوانیم از هر عبارت معتبر پایتون استفاده کنیم:
x = 'yes' if 42 else 'no'
x
'yes'
x = 'yes' if [] else 'no'
x
'no'
اینجا چه اتفاقی میافتد؟
قانون این است:
عباراتی که به صفر، دنبالهها یا ظرف های خالی (رشتهها، لیستها و غیره) و
Noneتشخیص داده میشوند، همه معادلFalseهستند.به عنوان مثال،
[]و()در یک شرطifمعادلFalseهستند
همه مقادیر دیگر معادل
Trueهستند.به عنوان مثال،
42در یک شرطifمعادلTrueاست
5.5.2. ترکیب عبارات#
میتوانیم عبارات را با استفاده از and، or و not ترکیب کنیم.
اینها ربطدهندههای منطقی استاندارد (عطف، فصل و نفی) هستند.
1 < 2 and 'f' in 'foo'
True
1 < 2 and 'g' in 'foo'
False
1 < 2 or 'g' in 'foo'
True
not True
False
not not True
True
به خاطر بسپارید:
P and QزمانیTrueاست که هر دوTrueباشند، در غیر این صورتFalseP or QزمانیFalseاست که هر دوFalseباشند، در غیر این صورتTrue
همچنین میتوانیم از ()all و ()any برای آزمایش دنبالهای از عبارات استفاده کنیم:
all([1 <= 2 <= 3, 5 <= 6 <= 7])
True
all([1 <= 2 <= 3, "a" in "letter"])
False
any([1 <= 2 <= 3, "a" in "letter"])
True
Note
()allزمانیTrueبرمیگرداند که همه مقادیر/عبارات boolean در دنبالهTrueباشند()anyزمانیTrueبرمیگرداند که هر مقدار/عبارت boolean در دنبالهTrueباشد
5.6. سبک کدنویسی و مستندسازی#
داشتن یک سبک کدنویسی سازگار و استفاده از مستندسازی میتواند کد را خواناتر و نگهداری و توسعه آن را آسان تر کند.
5.6.1. راهنمای سبک کدنویسی پایتون: PEP8#
میتوانید با تایپ کردن import this در خط فرمان با فلسفه برنامهنویسی پایتون آشنا شوید.
پایتون در کنار موارد دیگر، تأکید زیادی بر یکدست بودن و ثبات در سبک برنامه نویسی دارد.
همه ما ضربالمثل « سازگاری و ذهنهای کوچک » را شنیدهایم.
در برنامهنویسی، همانند ریاضیات، کاملا برعکس است.
یک مقاله ریاضی که در آن نمادهای \(\cup\) و \(\cap\) معکوس شده باشند، خواندن آن بسیار سخت خواهد بود، حتی اگر نویسنده در صفحه اول به شما بگوید.
در پایتون، سبک استاندارد در PEP8 بیان شده است.
(گاهی اوقات در این درس ها از PEP8 منحرف خواهیم شد تا نمادگذاری ریاضی را بهتر رعایت کنیم)
5.6.2. داکاسترینگها (docstring)#
پایتون سیستمی برای اضافه کردن توضیحات به ماژولها، کلاسها، توابع و غیره دارد که داکاسترینگ(docstring) نامیده میشود.
نکته خوب درباره داکاسترینگها این است که در زمان اجرا در دسترس هستند.
اجرای این کد را امتحان کنید:
def f(x):
"""
This function squares its argument
"""
return x**2
پس از اجرای این کد، داکاسترینگ در دسترس است:
f?
Type: function
String Form:<function f at 0x2223320>
File: /home/john/temp/temp.py
Definition: f(x)
Docstring: This function squares its argument
f??
Type: function
String Form:<function f at 0x2223320>
File: /home/john/temp/temp.py
Definition: f(x)
Source:
def f(x):
"""
This function squares its argument
"""
return x**2
با یک علامت سوال داکاسترینگ را میآوریم، و با دو علامت سوال کد منبع را نیز دریافت میکنیم.
میتوانید قراردادهای داکاسترینگ را در PEP257 پیدا کنید.
5.7. تمرینها#
تمرینهای زیر را حل کنید.
(برای برخی از تمرینات، تابع داخلی ()sum کاربردی است).
Exercise 5.1
قسمت 1: با توجه به دو لیست یا tuple عددی x_vals و y_vals با طول برابر، ضرب داخلی آنها را با استفاده از ()zip محاسبه کنید.
قسمت 2: در یک خط، تعداد اعداد زوج در بازه 0,…,99 را بشمارید.
قسمت 3: با توجه به pairs = ((2, 5), (4, 2), (9, 8), (12, 10))، تعداد جفتهای (a, b) را بشمارید که در آن هم a و هم b زوج باشند.
Hint
x % 2 اگر x زوج باشد 0 برمیگرداند، در غیر این صورت 1.
Solution to Exercise 5.1
راهحل قسمت 1:
یک راهحل ممکن میتواند این باشد:
x_vals = [1, 2, 3]
y_vals = [1, 1, 1]
sum([x * y for x, y in zip(x_vals, y_vals)])
6
این روش هم برای این سوال کار میکند:
sum(x * y for x, y in zip(x_vals, y_vals))
6
راهحل قسمت 2:
یک راهحل ممکن میتواند این باشد:
sum([x % 2 == 0 for x in range(100)])
50
این روش هم برای این سوال کار میکند:
sum(x % 2 == 0 for x in range(100))
50
برخی از جایگزینهای کمتر طبیعی که با این حال به نشان دادن انعطافپذیری خلاصه لیستها کمک میکنند عبارتند از:
len([x for x in range(100) if x % 2 == 0])
50
و
sum([1 for x in range(100) if x % 2 == 0])
50
راهحل قسمت 3:
یک راه حل این است:
pairs = ((2, 5), (4, 2), (9, 8), (12, 10))
sum([x % 2 == 0 and y % 2 == 0 for x, y in pairs])
2
Exercise 5.2
چندجملهای زیر را در نظر بگیرید:
تابع p را بنویسید به طوری که p(x, coeff) مقدار را در (5.1) با توجه به نقطه x و لیست ضرایب coeff (\(a_1, a_2, \cdots a_n\)) محاسبه کند.
سعی کنید از ()enumerate در حلقه خود استفاده کنید.
Solution to Exercise 5.2
یک راهحل این است:
def p(x, coeff):
return sum(a * x**i for i, a in enumerate(coeff))
p(1, (2, 4))
6
Exercise 5.3
تابعی بنویسید که یک رشته را به عنوان آرگومان میگیرد و تعداد حروف بزرگ در رشته را برمیگرداند.
Hint
'foo'.upper() مقدار 'FOO' را برمیگرداند.
Solution to Exercise 5.3
یک راهحل این است:
def f(string):
count = 0
for letter in string:
if letter == letter.upper() and letter.isalpha():
count += 1
return count
f('The Rain in Spain')
3
یک راه حل جایگزین و پایتونی تر:
def count_uppercase_chars(s):
return sum([c.isupper() for c in s])
count_uppercase_chars('The Rain in Spain')
3
Exercise 5.4
تابعی بنویسید که دو دنباله seq_a و seq_b را به عنوان آرگومان میگیرد و True را برمیگرداند اگر هر عنصر در seq_a همچنین عنصری از seq_b باشد، در غیر این صورت False.
منظور از “دنباله” یک لیست، یک tuple یا یک رشته است.
تمرین را بدون استفاده از setها و متدهای set انجام دهید.
Solution to Exercise 5.4
یک راهحل این است:
def f(seq_a, seq_b):
for a in seq_a:
if a not in seq_b:
return False
return True
# == test == #
print(f("ab", "cadb"))
print(f("ab", "cjdb"))
print(f([1, 2], [1, 2, 3]))
print(f([1, 2, 3], [1, 2]))
True
False
True
False
یک راه حل جایگزین، پایتونیتر با استفاده از ()all:
def f(seq_a, seq_b):
return all([i in seq_b for i in seq_a])
# == test == #
print(f("ab", "cadb"))
print(f("ab", "cjdb"))
print(f([1, 2], [1, 2, 3]))
print(f([1, 2, 3], [1, 2]))
True
False
True
False
البته، اگر از نوع داده sets استفاده کنیم، راهحل سادهتر است:
def f(seq_a, seq_b):
return set(seq_a).issubset(set(seq_b))
Exercise 5.5
وقتی کتابخانههای عددی را پوشش دهیم، خواهیم دید که آنها شامل جایگزینهای زیادی برای درونیابی و تقریب تابع هستند.
با این حال، بیایید روتین تقریب تابع خودمان را به عنوان یک تمرین بنویسیم.
به طور خاص، بدون استفاده از هیچ import، تابعی linapprox بنویسید که به عنوان آرگومانها میگیرد
یک تابع
fکه بازهای \([a, b]\) را به \(\mathbb R\) نگاشت میکند.دو اسکالر
aوbکه حدود این بازه را مشخص میکنند.یک عدد صحیح
nکه تعداد نقاط شبکه را تعیین میکند.یک عدد
xکهa <= x <= bرا برآورده میکند.
و درونیابی خطی تکهای f را در x، بر اساس n نقطه شبکه با فاصله یکسان a = point[0] < point[1] < ... < point[n-1] = b برمیگرداند.
برای وضوح تلاش کنید، نه کارایی.
Solution to Exercise 5.5
یک راهحل این است:
def linapprox(f, a, b, n, x):
"""
Evaluates the piecewise linear interpolant of f at x on the interval
[a, b], with n evenly spaced grid points.
Parameters
==========
f : function
The function to approximate
x, a, b : scalars (floats or integers)
Evaluation point and endpoints, with a <= x <= b
n : integer
Number of grid points
Returns
=======
A float. The interpolant evaluated at x
"""
length_of_interval = b - a
num_subintervals = n - 1
step = length_of_interval / num_subintervals
# === find first grid point larger than x === #
point = a
while point <= x:
point += step
# === x must lie between the gridpoints (point - step) and point === #
u, v = point - step, point
return f(u) + (x - u) * (f(v) - f(u)) / (v - u)
Exercise 5.6
با استفاده از سینتکس خلاصه لیست، میتوانیم حلقه در کد زیر را ساده کنیم.
import numpy as np
rng = np.random.default_rng()
n = 100
ϵ_values = []
for i in range(n):
e = rng.standard_normal()
ϵ_values.append(e)
Solution to Exercise 5.6
یک راهحل این است:
rng = np.random.default_rng()
n = 100
ϵ_values = [rng.standard_normal() for i in range(n)]