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

الگوریتم‌های لیست

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

در این فصل می‌خوانید
  1. مجموع و میانگین با دست
  2. پیدا کردن بیشینه
  3. پیدا کردن کمینه
  4. میانگین با شرط
  5. توابع آماده پایتون
  6. جست‌وجوی خطی
  7. مرتب‌سازی
  8. دام سایه‌اندازی نام
  9. لیست دوبعدی
  10. عملیات روی ماتریس
  11. برنامه کامل گزارش‌گیری

۱. مجموع و میانگین با دست

# Adding every item of a list, the manual way.
numbers = [200, 350, 40, 1000, 270]

total = 0
for number in numbers:
    total = total + number
    # short form: total += number

print('Total:', total)
print('Average:', total / len(numbers))

مجموع و میانگین: نمونه 01_sum_of_list.py

Total: 1860
Average: 372.0

همان الگوی انباره فصل ششم، این بار روی لیست به جای range. این الگو پایه همه الگوریتم‌های این فصل است.

۲. پیدا کردن بیشینه

منطقش شبیه مسابقه است: عضو اول را «قهرمان فعلی» فرض می‌کنیم و بقیه را یکی‌یکی با او مقایسه می‌کنیم. هر کس بهتر بود، قهرمان تازه می‌شود.

# Finding the biggest item.
# The trick: start with the FIRST item as the current champion,
# then compare it with every other item.
numbers = [30, 20, 45, 18, -7, 22]

largest = numbers[0]

for number in numbers:
    if number > largest:
        largest = number

print('Max:', largest)

# Starting from 0 instead of numbers[0] would be a bug:
# with a list of only negative numbers the answer would wrongly be 0.

بیشینه: نمونه 02_max_of_list.py

چرا نباید از صفر شروع کرد

وسوسه‌انگیز است که بنویسید largest = 0. ولی اگر همه اعداد لیست منفی باشند، هیچ‌کدام از صفر بزرگ‌تر نیستند و جواب صفر در می‌آید، در حالی که صفر اصلاً عضو لیست نیست. شروع از numbers[0] همیشه درست است، چون قطعاً یکی از اعضای واقعی است.

۳. پیدا کردن کمینه

# Same algorithm, with the comparison turned around.
numbers = [30, 20, 45, 18, -7, 22]

smallest = numbers[0]

for number in numbers:
    if number < smallest:
        smallest = number

print('Min:', smallest)

کمینه: نمونه 03_min_of_list.py

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

۴. میانگین با شرط

# Average of the even numbers only: we need a total AND a counter,
# because we do not know in advance how many even numbers there are.
numbers = [30, 20, 45, 18, -7, 22]

total = 0
counter = 0

for number in numbers:
    if number % 2 == 0:
        total += number
        counter += 1

# Guard against a list without any even number.
if counter == 0:
    print('There is no even number in the list.')
else:
    print('Average of the even numbers:', total / counter)

میانگین اعداد زوج: نمونه 04_average_of_evens.py

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

۵. توابع آماده پایتون

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

# Python already knows how to do all of that. Use the built-in functions:
# they are shorter, faster and impossible to get wrong.
numbers = [30, 20, 45, 18, -7, 22]

print('len:', len(numbers))
print('sum:', sum(numbers))
print('max:', max(numbers))
print('min:', min(numbers))
print('avg:', sum(numbers) / len(numbers))

# They work on strings too.
words = ['pear', 'apple', 'banana']
print(max(words))            # the last one in alphabetical order
print(min(words, key=len))   # the shortest word

توابع آماده: نمونه 05_builtin_functions.py

تابعوظیفه آن
len(lst)تعداد اعضا
sum(lst)مجموع. فقط روی عدد کار می‌کند
max(lst) min(lst)بزرگ‌ترین و کوچک‌ترین
max(lst, key=len)بزرگ‌ترین بر اساس معیار دلخواه
sorted(lst)لیست مرتب‌شده تازه

پارامتر key خیلی قدرتمند است: می‌گوید «مقایسه را بر اساس این معیار انجام بده». min(words, key=len) یعنی کوتاه‌ترین کلمه، نه کلمه‌ای که در الفبا اول است. در فصل بیست و ششم به این پارامتر برمی‌گردیم و کاملش می‌کنیم.

پس چرا با دست نوشتیم

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

۶. جست‌وجوی خطی

# Linear search: walk through the list until the value is found.
numbers = [30, 20, 45, 18, -7, 22]

target = int(input('Which number are you looking for? '))

found_at = -1                     # -1 means "not found yet"

for i in range(len(numbers)):
    if numbers[i] == target:
        found_at = i
        break                     # stop at the first match

if found_at == -1:
    print('Not found.')
else:
    print(f'Found at index {found_at}.')

# Python offers the same thing directly:
if target in numbers:
    print('index() says:', numbers.index(target))

جست‌وجوی خطی: نمونه 06_search.py

شرح مثال

۷. مرتب‌سازی

numbers = [30, 20, 45, 18, -7, 22]

# sorted() returns a NEW sorted list and leaves the original alone.
ordered = sorted(numbers)
print('sorted():', ordered)
print('original:', numbers)

# sort() sorts the list IN PLACE and returns None.
numbers.sort()
print('after sort():', numbers)

# Descending order.
numbers.sort(reverse=True)
print('descending:', numbers)

# reverse() just turns the list around, it does not sort.
numbers.reverse()
print('reversed:', numbers)

# Sorting text.
names = ['Reza', 'ali', 'Maryam']
print(sorted(names))                    # capitals come first
print(sorted(names, key=str.lower))     # case-insensitive order

مرتب‌سازی: نمونه 07_sorting.py

ابزارلیست اصلیخروجی
sorted(lst)دست‌نخوردهلیست تازه مرتب‌شده
lst.sort()خودش مرتب می‌شودNone
lst.reverse()خودش برعکس می‌شودNone
lst[::-1]دست‌نخوردهلیست تازه برعکس
اشتباهی که همه یک بار می‌کنند

نوشتن numbers = numbers.sort(). متد sort() هیچ چیز برنمی‌گرداند، پس None در متغیر می‌نشیند و لیستتان را کاملاً از دست می‌دهید. یا numbers.sort() تنها بنویسید، یا numbers = sorted(numbers).

درباره مرتب‌سازی متن: حروف بزرگ در جدول کاراکترها پیش از حروف کوچک می‌آیند، پس 'Reza' پیش از 'ali' قرار می‌گیرد. با key=str.lower این مشکل حل می‌شود.

۸. دام سایه‌اندازی نام

در فصل ششم به این اشاره کردیم؛ حالا که واقعاً از sum و max استفاده می‌کنیم، وقتش رسیده که کامل ببینیمش.

# 'sum', 'max', 'min', 'list', 'str', 'input' ... are names of built-in things.
# If you use them as variable names, the built-in version disappears.
numbers = [1, 2, 3]

sum = 0                 # BAD: the built-in function sum() is now hidden
for n in numbers:
    sum += n
print(sum)              # works ...

# ... but now this line would raise:
#   TypeError: 'int' object is not callable
# print(sum(numbers))

# Always prefer a descriptive name instead:
total = 0
for n in numbers:
    total += n
print(total)
print(sum)              # still the integer, the damage is done in this file

سایه‌اندازی: نمونه 08_shadowing_trap.py

نام ممنوعجایگزین پیشنهادی
sumtotal
max minlargest smallest
listitems numbers names
strtext
inputuser_input
typekind
iduser_id

نکته مهم: پایتون هیچ خطایی نمی‌دهد و اجازه می‌دهد این کار را بکنید. مشکل بعداً و در جای دیگری از برنامه ظاهر می‌شود، جایی که تازه‌کار به هیچ وجه دنبال علتش نمی‌گردد. ویرایشگرهای خوب مثل VS Code این نام‌ها را رنگ متفاوتی می‌دهند؛ اگر متغیرتان همان رنگ توابع آماده شد، نامش را عوض کنید.

۹. لیست دوبعدی

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

# A list whose items are themselves lists is a 2-D list (a matrix).
matrix = [
    [4, 17, 81],
    [11, 41, 17],
    [20, 12, 13],
]

# Two indexes: first the row, then the column.
print(matrix[1][1])      # 41
print(matrix[0])         # the whole first row

# A new row can be appended like any other item.
matrix.append([4, 5, 6])

# Printing a matrix needs two loops.
for row in matrix:
    for value in row:
        print(value, end='\t')
    print()

ماتریس: نمونه 09_two_dimensional_list.py

41
[4, 17, 81]
4	17	81	
11	41	17	
20	12	13	
4	5	6	

شرح مثال

۱۰. عملیات روی ماتریس

# Classic operations on a square matrix.
matrix = [
    [4, 17, 81],
    [11, 41, 17],
    [20, 12, 13],
]

SIZE = len(matrix)

# Total of every row.
for i in range(SIZE):
    print(f'sum of row {i}:', sum(matrix[i]))

# Total of every column: the column index stays fixed while the row moves.
for column in range(SIZE):
    column_total = 0
    for row in range(SIZE):
        column_total += matrix[row][column]
    print(f'sum of column {column}:', column_total)

# The main diagonal is made of the items where row == column.
diagonal_total = 0
for i in range(SIZE):
    diagonal_total += matrix[i][i]
print('sum of the diagonal:', diagonal_total)

# The biggest value of the whole matrix.
largest = matrix[0][0]
for row in matrix:
    for value in row:
        if value > largest:
            largest = value
print('largest value:', largest)

عملیات ماتریسی: نمونه 10_matrix_operations.py

شرح مثال

۱۱. برنامه کامل گزارش‌گیری

# A complete little program: read numbers until 'exit',
# then report several statistics about them.
numbers = []

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

    if user_input == 'exit':
        break

    numbers.append(float(user_input))

if len(numbers) == 0:
    print('Nothing to report.')
else:
    even_total = 0
    for number in numbers:
        if number % 2 == 0:
            even_total += number

    print('Count:  ', len(numbers))
    print('Sum:    ', sum(numbers))
    print('Average:', sum(numbers) / len(numbers))
    print('Max:    ', max(numbers))
    print('Min:    ', min(numbers))
    print('Sum of the even ones:', even_total)
    print('Sorted: ', sorted(numbers))

گزارش کامل: نمونه 11_sum_of_positive_input.py

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

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

پوشه S10-list-algorithms شامل نمونه‌های 01_sum_of_list.py تا 11_sum_of_positive_input.py.

خلاصه فصل

موردنکته
مجموع با دستانباره صفر، جمع در حلقه
بیشینه با دستشروع از lst[0]، نه از صفر
میانگین با شرطانباره و شمارنده جداگانه
sum max min len sortedنسخه آماده و بهتر همه این موارد
key=تعیین معیار مقایسه
جست‌وجوی خطیپرچم منفی یک، حلقه با اندیس، break
sort() و sorted()اولی خود لیست را، دومی لیست تازه
سایه‌اندازیهرگز متغیری به نام sum یا list نسازید
matrix[r][c]اول سطر، بعد ستون
جمع ستونستون ثابت، سطر متحرک

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

  1. ده عدد بگیرید و بزرگ‌ترین و کوچک‌ترین و میانگین را بدون max و min و sum چاپ کنید.
  2. لیستی از اعداد بگیرید و دومین عدد بزرگ را چاپ کنید.
  3. لیستی از نمره‌ها بگیرید و بگویید چند نفر قبول شده‌اند، یعنی نمره ۱۰ یا بیشتر.
  4. لیستی از نام‌ها را الفبایی مرتب کنید، بدون حساسیت به حروف بزرگ و کوچک.
  5. یک ماتریس سه در سه از کاربر بگیرید و سطر به سطر چاپش کنید.
  6. ترانهاده یک ماتریس را چاپ کنید، یعنی سطرها ستون شوند.
  7. دو لیست بگیرید و اعضایی که در هر دو هستند را چاپ کنید.
  8. لیستی از اعداد بگیرید و آن را به دو لیست زوج و فرد تقسیم کنید.
فصل قبل: لیست‌ها فصل بعد: توابع