שבוע 9 – יום 5: תרגילי סיכום

שבוע 9 – תרגול

None shall pass

בקובץ connected_users.txt מופיעים המשתמשים שמחוברים למערכת שלנו.
בכל שורה מופיע משתמש יחיד.

כתבו decorator שנקרא @login_required.
ה־decorator יניח שהפרמטר הראשון של הפונקציה אותה הוא “מקשט” הוא שם המשתמש.
אם שם המשתמש נמצא בקובץ connected_users.txt, הריצו את הפונקציה.
אם שם המשתמש לא נמצא שם, זרקו חריגת UserNotFoundError (אותה תצרו בעצמכם).

בונוס: דאגו ל־type annotations בתרגיל.
אנקדוטה משעשעת: אנחנו משתמשים ב־decorator שמבוסס על הרעיון הזה בקוד של מערכת בדיקת התרגילים.

קולונל האטי

הקוד הבא מחזיר את 40 האיברים הראשונים בסדרת פיבונאצ’י:

def fibo(n):
    if n <= 2:
        return 1
    return fibo(n - 1) + fibo(n - 2)


print([fibo(n) for n in range(1, 40)])

ממשו decorator בשם cache.
ה־decorator שתיצרו ידע לשמור את התוצאה ש־fibo מחזירה עבור n מסוים.
אם הפונקציה נקראה בעבר עם n זהה, cache יחזיר מיד את הערך השמור במקום לחשב אותו מחדש.

לדוגמה – אם נקרא ל־fibo(6) פעמיים:

  1. בקריאה הראשונה התוכנה שלנו תחשב את ערך הפיבונאצ’י השישי ותחזיר לנו 8.
  2. בקריאה השנייה cache יחזיר מייד את התוצאה 8, מבלי לקרוא באמת לפונקציה fibo שמחשבת את סדרת פיבונאצ’י.

איך תדעו שהצלחתם? הוספה של @cache מעל fibo אמורה לאפשר לכם להריץ את הקוד שלמעלה עם range(1, 100) ושזה יעבוד בצ’יק.

אחרי שהצלחתם, ודאו שה־decorator שלכם פועל על כל כמות פרמטרים שהיא (ב־fibo יש רק אחד, אבל עליכם לדאוג שה־decorator שלכם יהיה גמיש לכמות הפרמטרים שהוא מקבל).

רמז

בעת מימוש הפונקציה המקוננת לצורך ה־decorator, אפשר להגדיר משתנים בתוך הפונקציה החיצונית.
לפונקציה הפנימית יש גישה למשתנים הללו.

ביגבאגים

לפניכם תוכנית שמקבלת Iterable כלשהו של מספרים (נניח: [1, 6, 7, 3]) ותוצאה רצויה (נניח: 10).
התוכנית תחזיר את כל התרגילים החשבוניים שאפשר לבנות מהמספרים שקיבלה.

עבור הדוגמה שניתנה מעלה התוכנה אמורה להחזיר את הפלט הבא:

פלט לדוגמה
1*7+6-3
1*6+7-3
7*1+6-3
6*1+7-3
1*7-3+6
1*6-3+7
7*1-3+6
6*1-3+7
3*6-1-7
3*6-7-1
6*3-1-7
6*3-7-1
6/3+1+7
6/3+7+1
7/1+6-3
6/1+7-3
7/1-3+6
6/1-3+7
7+1*6-3
7+6*1-3
6+1*7-3
6+7*1-3
1+6/3+7
7+6/3+1
7+6/1-3
6+7/1-3
1+7+6/3
7+1+6/3
7+6-1*3
7+6-3*1
6+7-1*3
6+7-3*1
7+6-3/1
6+7-3/1
7-1*3+6
7-3*1+6
6-1*3+7
6-3*1+7
7-3/1+6
6-3/1+7
7-3+1*6
7-3+6*1
6-3+1*7
6-3+7*1
7-3+6/1
6-3+7/1

לתוכנית הוכנסו שלושה תקלים משמעותיים.
הוסיפו Type annotations לתוכנית ותקנו את התקלים שמופיעים בה.
היעזרו במנפה השגיאות של סביבת העבודה שלכם.

import itertools
from typing import NamedTuple


ALLOWED_OPERATORS = ('*', '/', '+', '-')  # , '**)
REPEAT_NUMBERS = False


def normalize_input(digits):
    if not all(map(str.isdecimal, digits)):
        raise ValueError("'Digits' must be consisted of decimal numbers.")
    return list(map(str, digits))


def any_order(items, length=None, allow_repeats=True):
    length = length or len(items)

    if allow_repeats:
        yield from itertools.product(items, repeat=length)
    else:
        yield from itertools.permutations(items, r=length)


def zip_to_str(expression):
    return ''.join(''.join(i) for i in expression)


def get_answer(digits, operators):
    zipped_expression = itertools.zip_longest(digits, operators, fillvalue='0')
    expression = zip_to_str(zipped_expression)
    try:
        return Answer(solution=eval(expression), expression=expression)
    except ZeroDivisionError:
        return Answer(solution=None, expression=expression)


def solver(allowed_digits, solution):
    assert isinstance(solution, int), "Solution must be a number."
    digits = normalize_input(allowed_digits)

    for operators in any_order(ALLOWED_OPERATORS, length=len(digits)):
        for digits_option in any_order(digits, allow_repeats=REPEAT_NUMBERS):
            answer = get_answer(digits_option, operators)
            if answer.solution == solution:
                yield answer.expression


if __name__ == '__main__':
    for solution in solver('1679', solution=10):
        print(solution)
9 לייקים

היי,

אנחנו צריכים ליצור את קובץ connected_users.txt?

7 לייקים