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

مكتمل

هناك عدد قليل من الخيارات المشتركة بين جميع التعليقات التوضيحية؛ في الوحدة الأخيرة، تعرفت على الخيارين 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 بالتحكم في عدد نسخ التعليقات التوضيحية المسماة المضمنة في تطبيق المرجع. يساعد هذا الخيار في الحالات التي يعاد فيها استخدام الوظائف التي تنشئ التعليقات التوضيحية عدة مرات خلال التعيين. الحالات التي تكون فيها بعض الاختبارات الأولية كافية للتحقق من صحة التنفيذ عن طريق تقليل حجم التنفيذ المرجعي نفسه.

دعونا نوضح هذا باستخدام دالتنا maximum . هنا، نستخدم تطبيقا مشابها للمرجع السابق ولكننا نضبط 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"Annotations created: {len(max_ref)}")
>>> max_ref = pybryt.ReferenceImplementation("maximum", max_ref)
>>> print(f"Annotations in reference: {len(max_ref.annotations)}")
Annotations created: 1000
Annotations 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")

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