خيارات التعليق التوضيحي

مكتمل

هناك عدد قليل من الخيارات المشتركة بين جميع التعليقات التوضيحية؛ في الوحدة الأخيرة، تعرفت على الخيارين success_message وfailure_message. في هذه الوحدة، سنناقش ثلاثة خيارات أخرى يمكن تطبيقها على التعليقات التوضيحية وكيفية استخدامها.

name

يتم استخدام الخيار name لتجميع مثيلات مختلفة من فئات التعليقات التوضيحية التي تمثل معًا نفس التعليق التوضيحي. يُستخدم هذا بشكل أساسي لمنع عرض الرسائل عدة مرات عندما لا تحتاج إلى ذلك. دعنا نفكر في مثال: الدالة maximum أدناه تستدعي فقط دالة Python المسماة max للتحقق من أن الطالب قد حدد الحد الأقصى بشكل صحيح، ولكن تتم طباعة رسالة نجاح واحدة لكل إدخال يتم اختبار الدالة عليه.

>>> max_ref = []
>>> def maximum(l, track=False):
...     m = max(l)
...     if track:
...         max_ref.append(pybryt.Value(
...             m,
...             success_message="Found the max!", 
...             failure_message="Did not find the max",
...         ))
...     return m
>>> test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
>>> for test_list in test_lists:
...     maximum(test_list, track=True)
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> with pybryt.check(max_ref):
...     for test_list in test_lists:
...         maximum(test_list)
REFERENCE: maximum
SATISFIED: True
MESSAGES:
  - Found the max!
  - Found the max!
  - Found the max!
  - Found the max!

المشكلة في هذا بسيطة: التعليق التوضيحي الذي يتم إنشاؤه في كل اختبار يتحقق بشكل أساسي من نفس الشيء، سواء أكان الطالب قد أعاد القيمة الصحيحة أم لا. إن طباعة نفس الرسالة عدة مرات يجعل الأمر يبدو وكأن التعليقات التوضيحية تختبر ظروفًا مختلفة، وتؤدي إلى تشويش التقرير الذي تم إنشاؤه بواسطة PyBryt. يمكننا طي كل هذه الرسائل معًا عن طريق تسمية التعليق التوضيحي الذي تم إنشاؤه في الدالة maximum:

>>> max_ref = []
>>> def maximum(l, track=False):
...     m = max(l)
...     if track:
...         max_ref.append(pybryt.Value(
...             m,
...             name="list-maximum",
...             success_message="Found the max!", 
...             failure_message="Did not find the max",
...         ))
...     return m
>>> test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
>>> for test_list in test_lists:
...     maximum(test_list, track=True)
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> with pybryt.check(max_ref):
...     for test_list in test_lists:
...         maximum(test_list)
REFERENCE: maximum
SATISFIED: True
MESSAGES:
  - Found the max!

الآن، يمكننا أن نرى أن الرسالة مطبوعة مرة واحدة فقط.

عندما يطوي PyBryt التعليقات التوضيحية في رسالة واحدة، فإنه سيعرض رسالة النجاح فقط إذا تم استيفاء جميع التعليقات التوضيحية في مجموعة الاسم؛ أما إذا فشل أي منها، فسيتم عرض رسالة الفشل بدلاً من ذلك. دعونا نقدم خطأ في maximum لإثبات هذا:

>>> def maximum(l):
...     if len(l) % 2 == 0:
...         m = min(l)
...     else:
...         m = max(l)
...     return m
>>> with pybryt.check(max_ref):
...     for test_list in test_lists:
...         maximum(test_list)
REFERENCE: maximum
SATISFIED: False
MESSAGES:
  - Did not find the max

limit

يسمح لك الخيار limit بالتحكم في عدد نسخ التعليقات التوضيحية المسماة المضمنة في تطبيق المرجع. يساعد هذا في الحالات التي يتم فيها إعادة استخدام الدالات التي تنشئ التعليقات التوضيحية عدة مرات خلال مهمة ما، لكن بعض الاختبارات الأولية كافية للتحقق من صحة التنفيذ عن طريق تقليل حجم تنفيذ المرجع نفسه.

دعونا نوضح هذا باستخدام أقصى دالة لدينا. سنستخدم تطبيقًا مشابهًا للمرجع أعلاه، ولكن مع تعيين limit على خمسة تعليقات توضيحية واختباره في عدة قوائم إدخال.

>>> max_ref = []
>>> def maximum(l, track=False):
...     m = max(l)
...     if track:
...         max_ref.append(pybryt.Value(
...             m,
...             name="list-maximum",
...             limit=5,
...             success_message="Found the max!", 
...             failure_message="Did not find the max",
...         ))
...     return m
>>> for _ in range(1000):
...     test_list = np.random.normal(size=100)
...     maximum(test_list, track=True)
>>> print(f"Anntoations created: {len(max_ref)}")
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> print(f"Anntoations in reference: {len(max_ref.annotations)}")
Anntoations created: 1000
Anntoations in reference: 5

كما ترى، طول max_ref.annotations يبلغ 5 على الرغم من تضمين 1000 تعليق توضيحي في القائمة التي تم تمريرها إلى الدالة الإنشائية.

group

يتشابه كل من الخيار group وname في أنه يُستخدم لتجميع التعليقات التوضيحية معًا، ولكن هذه التعليقات التوضيحية لا تمثل بالضرورة "نفس التعليق التوضيحي"؛ وبدلاً من ذلك، يتم تجميعها في مجموعات ذات معنى بحيث يمكن التحقق من أجزاء معينة من المراجع واحدة تلو الأخرى بدلاً من الكل مرة واحدة. يمكن أن يكون هذا مفيدًا في إنشاء التعيينات مع أسئلة متعددة في PyBryt.

على سبيل المثال، فكر في واجب بسيط يطلب من الطلاب تنفيذ وظيفة mean وmedian. يمكنك تقسيمه إلى سؤالين مثل:

# Question 1
mean_ref = []

def mean(l, track=False):
    size = len(l)
    if track:
        mean_ref.append(pybryt.Value(
            size,
            name="len",
            group="mean",
            success_message="Determined the length of the list",
        ))

    m = sum(l) / size
    if track:
        mean_ref.append(pybryt.Value(
            m,
            name="mean",
            group="mean",
            success_message="Calculated the correct mean of the list",
            failure_message="Did not find the correct mean of the list",
        ))

    return m

# Question 2
median_ref = []

def median(l, track=True):
    sorted_l = sorted(l)
    if track:
        median_ref.append(pybryt.Value(
            sorted_l,
            name="sorted",
            group="median",
            success_message="Sorted the list",
        ))
    
    size = len(l)
    if track:
        mean_ref.append(pybryt.Value(
            size,
            name="len",
            group="median",
            success_message="Determined the length of the list",
        ))

    middle = size // 2
    is_set_size_even = size % 2 == 0

    if is_set_size_even:
        m = (sorted_l[middle - 1] + sorted_l[middle]) / 2
    else:
        m = sorted_l[middle]

    if track:
        mean_ref.append(pybryt.Value(
            m,
            name="mean",
            group="mean",
            success_message="Calculated the correct mean of the list",
            failure_message="Did not find the correct mean of the list",
        ))

    return m

test_lists = [[1, 2, 3], [-1, 0, 1], [10, -4, 2, 0], [1]]
for test_list in test_lists:
    mean(test_list, track=True)
    median(test_list, track=True)

assignment_ref = pybryt.ReferenceImplementation("mean-median", [*mean_ref, *median_ref])

باستخدام مرجع تم إنشاؤه على النحو الوارد أعلاه، يمكننا منح الطلاب فرصة للتحقق من عملهم في كل سؤال على حدة قبل الانتقال إلى السؤال التالي عن طريق إخبار PyBryt بمجموعة التعليقات التوضيحية التي يجب مراعاتها:

>>> with pybryt.check(assignment_ref, group="mean"):
...     for test_list in test_lists:
...         mean(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
  - Determined the length of the list
  - Calculated the correct mean of the list
>>> with pybryt.check(assignment_ref, group="median"):
...     for test_list in test_lists:
...         median(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
  - Determined the length of the list
  - Sorted the list
>>> with pybryt.check(assignment_ref):
...     for test_list in test_lists:
...         mean(test_list)
...         median(test_list)
REFERENCE: mean-median
SATISFIED: True
MESSAGES:
  - Determined the length of the list
  - Calculated the correct mean of the list
  - Sorted the list

اختبر معلوماتك

1.

أي من الاستدعاءات التالية يحدد عدد المرات التي يتم فيها تتبع المتغير lst خمس مرات؟

2.

ما هو الغرض من الخيار group؟

3.

لنفترض أننا أنشأنا مرجعًا بالتعليمات البرمجية التالية:

for i in range(10):
    pybryt.Value(foo(i), name="foo", limit=5)
    pybryt.Value(bar(i), group="foo")
    pybryt.Value(baz(i), name="baz")

كم عدد التعليقات التوضيحية في المرجع؟