تطبيقات

طول الخط المستقيم بين نقطتين

في هذا المثال نعرف نقطتين ثم نحسب المسافة بينهما. والمسافة الإقليدية بين نقطتين \((x_1, y_1)\) و \((x_2, y_2)\) تتبع معادلة فيثاغورس:

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

وتذكر أن:

\[ \sqrt{x} = x^{1/2} \]

def euclidean_distance(x1: float, y1: float, x2: float, y2: float) -> float:
    return ((x2 - x1)**2 + (y2 - y1)**2) ** 0.5

print(euclidean_distance(x1=0, y1=0, x2=3, y2=4))
print(euclidean_distance(1, 1, -2, -2))
5.0
4.242640687119285

تجزئة البيانات

هذا الإجراء يقسم قائمة إلى جزئين بنسبة محددة:

def split(data: list, ratio: float) -> tuple[list, list]:
    idx = int(len(data) * ratio)
    return data[:idx], data[idx:]

نختبر الإجراء ونلاحظ أن الناتج من نوع صف (tuple). فنستعمل التعيين المتعدد لاستخراج القيم من الصف:

xs = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
a, b = split(xs, 0.80)

assert a == [10, 20, 30, 40, 50, 60, 70, 80]
assert b == [90, 100]

سحب الرصيد

نريد أن نعرف إجراء سحب الرصيد لأي مستخدم ولأي جهاز صراف آلي ولأي مبلغ:

  1. أولاً، تأكد من وجود رصيد كافٍ لدى المستخدم.
  2. إذا كان الأمر كذلك، فتأكد من وجود رصيد كافٍ في جهاز الصراف الآلي لصرف المبلغ المطلوب.
def withdraw_cash(balance, amount, atm_cash):
    if balance >= amount:
        if atm_cash >= amount:
            balance -= amount
            atm_cash -= amount
            print("Withdrawal successful!")
        else:
            print("ATM does not have enough cash.")
    else:
        print("Insufficient balance.")

withdraw_cash(500, 200, 1000)
withdraw_cash(500, 200, 100)
withdraw_cash(500, 600, 1000)
Withdrawal successful!
ATM does not have enough cash.
Insufficient balance.

الكلمة المتناظرة

في هذا المثال نوضح استعمال مؤشرين على نفس النص لمعرفة ما إذا كانت الكلمة متناظرة:

  • المؤئر الأول بإصبعك الأيمن: i يبدأ من أول حرف وينتهي عند المنتصف
  • المؤئر الثاني بإصبعك الأيسر: j يبدأ من آخر حرف وينتهي عند المنتصف
def is_palindrome(word):
    """
    A palindrome word is one that can be read the same way from both ends.
    """

    # إزالة الاختلافات في الحروف الكبيرة والصغيرة
    word = word.lower()
    
    # إزالة الفواصل (لتطابق الكلمات مثل: "Race car")
    word = word.replace(" ", "")

    # التحقق من أن الكلمة متناظرة
    for i in range(len(word) // 2):
        j = len(word) - i - 1
        if word[i] != word[j]:
            return False
    return True

# الاختبارات
assert is_palindrome("radar")
assert is_palindrome("level") 
assert is_palindrome("madam")
assert is_palindrome("توت")
assert is_palindrome("خوخ")
assert is_palindrome("Race car")
assert is_palindrome("حصان ناصح")
assert not is_palindrome("python")

بدل الحلقة، وكان لنا أن نكتب باختصار: word == word[::-1] وهي تعني أن النص متناظر إذا كان مساوياً لنفسه بالمقلوب.

def is_palindrome(word):
    word = word.lower()
    word = word.replace(" ", "")
    return word == word[::-1]

تصحيح الإملاء

اكتب فعلًا يصحح الإخطاء الشائعة في الإملاء العربي.

  • إبدال التاء المربوطة بالهاء: مكتبه، لعبه، روايه
  • إبدال الظاء بالضاد والعكس: ظابط، قرظ، ضهر
  • زيادة الياء: أحسنتي، رأيتكي

والمطلوب فقط تصحيح الأخطاء المذكورة.

corrections_map = {
    'مكتبه': 'مكتبة',
    'لعبه': 'لعبة',
    'روايه': 'رواية',
    'ظابط': 'ضابط',
    'قرظ': 'قرض',
    'ضهر': 'ظهر',
    'أحسنتي': 'أحسنت',
    'رأيتكي': 'رأيتك',
}

def correct_spelling(sentence: str) -> str:
    words = sentence.split()
    result = []
    for word in words:
        al = word.startswith('ال')
        if al:
            word = word[2:]
        correction = corrections_map.get(word, word)
        if al:
            correction = 'ال' + correction
        result.append(correction)
    return ' '.join(result)
assert (
    correct_spelling('المكتبه فيها الروايه التي أبحث عنها') ==
    'المكتبة فيها الرواية التي أبحث عنها'
)

assert (
    correct_spelling('أعطاك الظابط القرظ بعد الضهر') ==
    'أعطاك الضابط القرض بعد الظهر'
)

assert (
    correct_spelling('رأيتكي أحسنتي') == 'رأيتك أحسنت')

ضم الأقران

ماذا لو أردت أن تضم قائمة من الأشخاص بحيث يكون فارق العمر بينهم أقل ما يمكن؟

Ahmad: 24
Mohannad: 17
Mohammed: 16
Salem: 32
Ali: 26
Samir: 31

أولاً سنرتب القائمة بناءً على العمر، ثم نأخذ العناصر اثنين اثنين، وبذلك نكون حققنا الهدف.

def make_pairs_by_age_diff(items: list[tuple[str, int]]) -> list[tuple[str, str]]:
    """Make pairs of people by their age difference minimum."""
    ages = [age for name, age in items]
    ages.sort()
    items_sorted = []
    for age in ages:
        for i in items:
            if i[1] == age:
                items_sorted.append(i)
    pairs = []
    for i in range(len(items_sorted) - 1):
        pairs.append((items_sorted[i], items_sorted[i+1]))
    return pairs
peers = [
    ("Ahmad", 24),
    ("Mohannad", 17),
    ("Mohammed", 16),
    ("Salem", 32),
    ("Ali", 26),
    ("Samir", 31),
]

الاختبارات

pairs = make_pairs_by_age_diff(peers)
for p in pairs:
    if "Ahmad" in p:
        assert "Ali" in p
    if "Mohannad" in p:
        assert "Mohammed" in p
    if "Salem" in p:
        assert "Samir" in p

التوفيق بين مجموعتين

مسألة التوفيق بين مجموعتين تجدها في سياقات كثيرة.

نريد في هذا التمرين التوفيق بين أشخاص وما لديهم من مهارات وبين شركات وما تطلبها من هذه المهارات.

يمكن أن تبدأ بهذا الشكل لإيجاد كمية التوافق:

have = {"A", "X", "Y"}
want = {"A", "B", "C", "D"}
print("نسبة الإعجاب:", len(set.intersection(have, want)) / len(want))
نسبة الإعجاب: 0.25

وفي العادة تكون البيانات مضمنة في قائمة على النحو التالي:

haves = [
    {
        "name": "Ahmad",
        "skills": [
            "Python",
            "Go",
            "JavaScript",
        ],
    },
    {
        "name": "Jawad",
        "skills": [
            "Negotiation",
            "Communication",
            "Business",
            "Marketing",
        ],
    },
]

wants = [
    {
        "job": "Data Scientist",
        "requirements": [
            "Python",
            "Machine Learning",
            "Statistics",
        ],
    },
    {
        "job": "Sales Manager",
        "requirements": [
            "Negotiation",
            "Communication",
        ],
    },
]

نعالجها لكل عنصر في قائمة wants:

ratios = {}
for want in wants:
    for have in haves:
        ratio = len(set.intersection(set(want["requirements"]), set(have["skills"]))) / len(want["requirements"])
        if want['job'] not in ratios:
            ratios[want['job']] = {}
        ratios[want['job']][have['name']] = ratio
ratios
{'Data Scientist': {'Ahmad': 0.3333333333333333, 'Jawad': 0.0},
 'Sales Manager': {'Ahmad': 0.0, 'Jawad': 1.0}}

الآن نرتب كل وظيفة بحسب الأعلى توافقًا.

أولاً نكتب دالة ترتِّب قاموسًا، ثم سنستعملها:

def sort_nested_dict(d: dict) -> dict:
    """Sorts a dictionary by values"""
    result = {}
    for key in d.keys():
        result[key] = {}
        values = [v for v in d[key].values()]
        values.sort(reverse=True)
        for v1 in values:
            for k, v2 in d[key].items():
                if v2 == v1:
                    result[key][k] = v2
    return result
sorted_ratios = sort_nested_dict(ratios)
sorted_ratios
{'Data Scientist': {'Ahmad': 0.3333333333333333, 'Jawad': 0.0},
 'Sales Manager': {'Jawad': 1.0, 'Ahmad': 0.0}}

ولكثرة الحاجة لترتيب البيانات، فإن إجراء sorted متعدد الاستعمالات في بايثون. انظر Sorting Basics و Key Functions من مرجع بايثون تحت عنوان: “HOW TO”.

for k, v in ratios.items():
    sorted_ratios[k] = sorted(v.items(), key=lambda x: x[1], reverse=True)
sorted_ratios
{'Data Scientist': [('Ahmad', 0.3333333333333333), ('Jawad', 0.0)],
 'Sales Manager': [('Jawad', 1.0), ('Ahmad', 0.0)]}

فأول المرشحين في علوم البيانات هو:

sorted_ratios["Data Scientist"][0][0]
'Ahmad'

وأول المرشحين في المبيعات هو:

sorted_ratios["Sales Manager"][0][0]
'Jawad'