تطبيقات

إن تطبيقات البيانات المتسلسلة كثيرة جدًا وواقعية إلى حد كبير. ولن نتطرق الآن إلى كيفية وصول هذه البيانات إلى بايثون (فذلك موضوع له تفاصيله)، لكننا سنتعامل مع البيانات باعتبار أنها وصلت إلينا.

ترتيب قائمة الأسماء

لو كان لدينا قائمة من الأسماء ونريد ترتيبها أبجديًّا؛ فإننا نستعمل الإجراء sort() الذي يأخذ القائمة كمفعول به ويرتبها في مكانها:

names = [
    "Yousef",
    "Ali",
    "Belal",
]
list.sort(names)
names
['Ali', 'Belal', 'Yousef']

ضم القوائم

تأتي البيانات عادة من مصادر متعددة. فلو كان لدينا قائمتان لدرجات الطلاب، بحيث تحتوي القائمة أزواجًا تحوي اسم الطالب ودرجته، فإننا نريد ضمها جميعًا في قائمة واحدة. وهذا يتحقق بعملية الجمع + بين القوائم:

class1_marks = [
    ("Yousef", 80),
    ("Ali", 70),
    ("Belal", 90),
]
class2_marks = [
    ("Dawood", 85),
    ("Muhammad", 75),
    ("Yaser", 95),
]
student_marks = class1_marks + class2_marks
student_marks
[('Yousef', 80),
 ('Ali', 70),
 ('Belal', 90),
 ('Dawood', 85),
 ('Muhammad', 75),
 ('Yaser', 95)]

نتأكد من عدد الطلاب:

assert len(student_marks) == len(class1_marks) + len(class2_marks)

ترتيب قائمة صفوف

والآن نريد أن نرتب هذه القائمة تصاعديًّا بحسب الدرجات.

# أولاً سنفصل قائمة الدرجات لوحدها
marks = [mark for name, mark in student_marks]

result = []
# نأتي على قائمة الدرجات بشكل تصاعدي
for mark in sorted(marks, reverse=True):
    # نبحث عن الطلاب الذين حصلوا على هذه الدرجة
    for row in student_marks:
        # إذا كانت الدرجة متطابقة
        if row[1] == mark:
            # نضيفهم إلى النتيجة
            result.append(row)
result
[('Yaser', 95),
 ('Belal', 90),
 ('Dawood', 85),
 ('Yousef', 80),
 ('Muhammad', 75),
 ('Ali', 70)]

ولو أردنا أسماء الثلاثة الأوَل:

high = [x[0] for x in result[:3]]
high
['Yaser', 'Belal', 'Dawood']

ونستطيع أن نأخذ أسماء الطلبة الأضعف في الدرجات حتى نفهم الأسباب ونحاول أن نرفع من مستواهم الدراسي. ولاحظ هنا نستعمل فك الأقواس بدل تحديد الموضع بالرقم:

low = [name for name, mark in result[-3:]]
low
['Yousef', 'Muhammad', 'Ali']

توزيع نماذج الاختبار

تصور أن لدينا نموذجين من الاختبار، ونريد أن نوزعها بحيث يأخذ نصفهم النموذج الأول والنصف الثاني يأخي النموذج الثاني، لكن نريد أن يكون التوزيع بحسب الصف الذي يجلس فيه الطلاب.

نفترض أن كل صف يجلس فيه ثلاثة طلاب، ونريد أن نجعل لكل صف نموذجًا.

students = [
    'Ahmad',
    'Belal',
    'Camal',
    'Dawood',
    'Emad',
    'Faris',
    'Ghaith',
    'Hussain',
]

نمر على قائمة الأسماء بخطوة مقدارها 3 (عدد الطلاب في كل صف):

for i in range(0, len(students), 3):
    print(students[i:i+3])
['Ahmad', 'Belal', 'Camal']
['Dawood', 'Emad', 'Faris']
['Ghaith', 'Hussain']

والآن نريد أن نحدد لكل صف نموذجًا. نحقق ذلك بعملية باقي القسمة لمعرفة ما إذا كان العدد يقبل القسمة على 2؛ إذ به يعرف الزوجي، والآخر هو الفردي:

for i in range(0, len(students), 3):
    if i % 2 == 0:
        print(students[i:i+3], 'test1')
    else:
        print(students[i:i+3], 'test2')
['Ahmad', 'Belal', 'Camal'] test1
['Dawood', 'Emad', 'Faris'] test2
['Ghaith', 'Hussain'] test1

فرز البيانات

تصور أن لدينا قائمة من الأرقام الموجبة والسالبة في مجموعة واحدة، ونريد فصلها لمجموعتين:

numbers = [-11, -12, 13, 14, -15, 16, 17, -18, -19, 20]
positives = []
negatives = []

for x in numbers:
    if x > 0:
        positives.append(x)
    else:
        negatives.append(x)

print('positives:', positives)
print('negatives:', negatives)
positives: [13, 14, 16, 17, 20]
negatives: [-11, -12, -15, -18, -19]

أو باستعمال الإنشاء المختصر:

numbers = [-11, -12, 13, 14, -15, 16, 17, -18, -19, 20]
positives = [x for x in numbers if x > 0]
negatives = [x for x in numbers if x < 0]

print('positives:', positives)
print('negatives:', negatives)
positives: [13, 14, 16, 17, 20]
negatives: [-11, -12, -15, -18, -19]

المحاسبة المالية

افترض أن لديك قائمة بالمصروفات والإيرادات لكل رُبع من السنة، وتريد حساب صافي الربح لكل ربع على حدة، ثم جمعها لتحصل على الربح الإجمالي للسنة.

income  = [52000, 51000, 48000, 50000]
expense = [46800, 45900, 43200, 47000]
net     = [    0,     0,     0,     0]
for i in range(len(income)):
    net[i] = income[i] - expense[i]

print("quarterly net:", net)
print("   annual net:", sum(net))
quarterly net: [5200, 5100, 4800, 3000]
   annual net: 18100

أو باستعمال zip للتكرار على مضموم القائمتين:

income  = [52000, 51000, 48000, 50000]
expense = [46800, 45900, 43200, 47000]
net = []
for inc, exp in zip(income, expense):
    net.append(inc - exp)

print("quarterly net:", net)
print("   annual net:", sum(net))
quarterly net: [5200, 5100, 4800, 3000]
   annual net: 18100

أو باستعمال الإنشاء المختصر:

income  = [52000, 51000, 48000, 50000]
expense = [46800, 45900, 43200, 47000]
net = [inc - exp for inc, exp in zip(income, expense)]

print("quarterly net:", net)
print("   annual net:", sum(net))
quarterly net: [5200, 5100, 4800, 3000]
   annual net: 18100

أسعار السلة

لديك قائمة بالأسعار:

prices = [
    ('apple', 10),
    ('banana', 20),
    ('orange', 30),
    ('mango', 40),
    ('pineapple', 50),
]

وتريد حساب مجمل سلة مشتريات لأحد الزبائن:

basket1 = [
    ('apple', 2),
    ('mango', 1)
]
basket2 = [
    ('banana', 1),
    ('orange', 2),
    ('pineapple', 1),
]

نكرر على العناصر

total_basket1 = 0
for fruit, count in basket1:
    for label, price in prices:
        if fruit == label:
            total_basket1 += price * count
print(total_basket1)
60

وكذلك للسلة الثانية:

total_basket2 = 0
for fruit, count in basket2:
    for label, price in prices:
        if fruit == label:
            total_basket2 += price * count
print(total_basket2)
130

إدارة الطلبات

orders = []

وتدخل الطلبات بإجراء الإلحاق append للقائمة:

x = (('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM')
orders.append(x)

وهكذا الطلبات اللاحقة:

orders.append((('falafel', 'hummus', 'tea'), '8:48 PM'))
orders.append((('kabab', 'tabbouleh', 'water'), '8:52 PM'))

وإننا حين ننجز الطلبات أو نعرضها، يكون بالترتيب التي دخلت به من الأول:

for order in orders:
    print(order)
(('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM')
(('falafel', 'hummus', 'tea'), '8:48 PM')
(('kabab', 'tabbouleh', 'water'), '8:52 PM')

وإذا أنجزنا الطلب (بالترتيب)، نزيله من القائمة:

finished = orders.pop(0)
print('done:', finished)

print('remaining:')
for order in orders:
    print(order)
done: (('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM')
remaining:
(('falafel', 'hummus', 'tea'), '8:48 PM')
(('kabab', 'tabbouleh', 'water'), '8:52 PM')

أو هب أن الزبون الأخير ألغى الطلب، فإننا نزيله هكذا:

cancelled = orders.pop(-1)
print('cancelled:', cancelled)

print('remaining:')
for order in orders:
    print(order)
cancelled: (('kabab', 'tabbouleh', 'water'), '8:52 PM')
remaining:
(('falafel', 'hummus', 'tea'), '8:48 PM')

ملاحظة: pop(-1) تساوي pop() إذ القيمة الابتدائية هي -1.

إزالة العناصر المتكررة

هب أننا وجدنا الطلب أدخل بالخطأ مرتين، ونريد إزالة التكرارات الموجودة في قائمة الطلبات كلها:

orders = [
    (('falafel', 'hummus', 'tea'), '8:42 PM'),
    (('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM'),
    (('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM'),
    (('falafel', 'hummus', 'tea'), '8:50 PM'),
]

لاحظ أن الطلب الأول والأخير ليسا مكررين، وذلك لأنهما في وقتين مختلفين، أم الطلبان في الوسط فهما في نفس الوقت بالضبط، ونريد إزالة مثل هذا التكرار:

result = []
for order in orders:
    if order not in result:
        result.append(order)
result
[(('falafel', 'hummus', 'tea'), '8:42 PM'),
 (('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM'),
 (('falafel', 'hummus', 'tea'), '8:50 PM')]

تعديل الطلب

نريد أن نتابع حالة الطلب، ونعدلها إذا أنهينا الطلب. لن نستطيع ذلك إذا حاولنا تعديل الصف ()، بل يجب أولًا أن نجعل قائمة الطلبات مكونة من قوائم [] وليس من صفوف:

orders = [
    [('falafel', 'hummus', 'tea'), '8:42 PM', 'PENDING'],
    [('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM', 'PENDING'],
    [('falafel', 'hummus', 'tea'), '8:50 PM', 'PENDING'],
]
orders[0][2] = 'DONE'

والآن نستطيع فرز الطلبات بحسب حالة الطلب:

orders_done = [order for order in orders if order[2] == 'DONE']
orders_pending = [order for order in orders if order[2] == 'PENDING']

print('done:')
for order in orders_done:
    print(order)

print('pending:')
for order in orders_pending:
    print(order)
done:
[('falafel', 'hummus', 'tea'), '8:42 PM', 'DONE']
pending:
[('kabab', 'kabab', 'shawerma', 'water', 'water'), '8:45 PM', 'PENDING']
[('falafel', 'hummus', 'tea'), '8:50 PM', 'PENDING']

معرفة النقاط المجاورة

تقول الرياضيات أن المسافة الإقليدية (على سطح مستوي لا متعرج) بين نقطتين تحكمها المعادلة:

\[ d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} \]

نريد استعماله لقائمة من النقاط لمعرفة النقاط المجاورة.

points = [(1, 2), (5, 6), (7, 8), (3, 4)]
x1, y1 = (2, 4)

distances = []

for x2, y2 in points:
  d = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
  distances.append(d)
  print(f'distance from {x1, y1} to {x2, y2} is {d}')

min_distance = min(distances)
min_distance_index = distances.index(min_distance)

print('min distance index:', min_distance_index, 'with distance:', min_distance)

nearest_point = points[min_distance_index]

print(f'nearest point to {x1, y1} is {nearest_point}')
distance from (2, 4) to (1, 2) is 2.23606797749979
distance from (2, 4) to (5, 6) is 3.605551275463989
distance from (2, 4) to (7, 8) is 6.4031242374328485
distance from (2, 4) to (3, 4) is 1.0
min distance index: 3 with distance: 1.0
nearest point to (2, 4) is (3, 4)

أمثلة إضافية على الإنشاء المختصر

يمكن استعمال اختصار من اختصار على النحو التالي. في هذا المثال نفرد قائمة مكونة من قوائم عددية، فنحوِّلها لتكون قائمة واحدة مكوَّنة من جميع هذه الأرقام:

vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

إذا كتبناها من غير اختصار فشكلها كالتالي (لاحظ استعملنا متغير flat لتجميع القيَم المفردة):

flat = []
for elem in vec:
    for num in elem:
        flat.append(num)
flat
[1, 2, 3, 4, 5, 6, 7, 8, 9]

وهذا مثال لحاصل الضرب الديكارتي بين مجموعتين رياضيتين:

colors = ['red', 'green']
shapes = ['circle', 'square']
cartesian_product = [(color, shape) for color in colors for shape in shapes]
cartesian_product
[('red', 'circle'),
 ('red', 'square'),
 ('green', 'circle'),
 ('green', 'square')]

راجع التوثيق لمزيد من الأمثلة.