關聯式註釋

已完成

PyBryt 中最基本的註釋種類是值註釋,其會判斷學生記憶體使用量中存在一些值。 不過,這些註釋並不涵蓋所有案例;如果您想要檢查某些值的兩個可能表示,或想要檢查值的順序,該怎麼辦? 這些情況就是關聯式註釋的用武之地。

關聯式註釋會定義其他註釋之間的某種關聯性。 您可以將關聯式註釋視為對其他註釋上運作的註釋,判斷如何滿足註釋的條件。 在上一個課程模組中,您已了解集合註釋,以及如何使用這些註釋來強制執行註釋的排序。 關聯式註釋很類似,事實上,您可以將集合視為關聯式註釋的類型。

PyBryt 目前支援兩種類型的關聯式註釋:時態性註釋和邏輯註釋。

時態性註釋

時態性註釋述不同註釋之間的時態關聯性。 滿足這些註釋的所有子註釋,而且滿足這些註釋之值的時間戳記會以特定順序發生時,就會滿足這些註釋。

PyBryt 只具有一種時態性註釋類型:BeforeAnnotation,其會判斷滿足其子註釋的時間戳記會以非遞減順序發生。 就像任何其他註釋一樣,您可以直接使用建構函式來具現化這些註釋,不過所有註釋也有 beforeafter 方法,可用來以更語意的方式建構這些註釋:

>>> a1 = pybryt.Value(1)
>>> a2 = pybryt.Value(2)
>>> a1.before(a2), a1.after(a2)
(pybryt.BeforeAnnotation, pybryt.BeforeAnnotation)

如您所見,Annotation.beforeAnnotation.after 會傳回 BeforeAnnotation,但註釋的順序會在 after 所傳回的註釋中反轉。

當您建立關聯式註釋時,您可以視需要更新每個註釋選項的欄位,或將選項當做關鍵字引數傳遞至 beforeafter 方法:

a1_before_a2 = a1.before(
    a2,
    success_message="a1 is before a2",
    failure_message="a1 is not before a2",
)

# or:
a1_before_a2 = a1.before(a2)
a1_before_a2.success_message = "a1 is before a2"
a1_before_a2.failure_message = "a1 is not before a2"

透過虛擬記憶體使用量,我們可以看到滿足 BeforeAnnotation 的方式。 在下列範例中,我們會使用 pybryt.MemoryFootprint.from_values 方法建立這類使用量,此方法接受替代的值和時間戳記:

pybryt.MemoryFootprint.from_values(val1, ts1, val2, ts2, val3, ts3, ...)

在我們變更使用量及其時間戳記中的值時,請注意關聯式註釋的結果會如何改變。

>>> ref = pybryt.ReferenceImplementation("temporal-annotations", [a1_before_a2])
>>> # the values in the correct order
>>> res = ref.run(pybryt.MemoryFootprint.from_values(1, 1, 2, 2))
>>> print(pybryt.generate_report(res))
REFERENCE: temporal-annotations
SATISFIED: True
MESSAGES:
  - a1 is before a2
>>> # put both values at the same timestamp
>>> res = ref.run(pybryt.MemoryFootprint.from_values(1, 1, 2, 1))
>>> print(pybryt.generate_report(res))
REFERENCE: temporal-annotations
SATISFIED: True
MESSAGES:
  - a1 is before a2
>>> # put the timestamp of 1 after the timestamp of 2
>>> res = ref.run(pybryt.MemoryFootprint.from_values(1, 2, 2, 1))
>>> print(pybryt.generate_report(res))
REFERENCE: temporal-annotations
SATISFIED: False
MESSAGES:
  - a1 is not before a2
>>> # don't satisfy the second annotation
>>> res = ref.run(pybryt.MemoryFootprint.from_values(1, 1))
>>> print(pybryt.generate_report(res))
REFERENCE: temporal-annotations
SATISFIED: False
MESSAGES:
  - a1 is not before a2

邏輯註釋

邏輯註釋與滿足註釋時的時態性無關,而是會對是否完全滿足註釋進行處理。 其會判斷是否滿足子註釋的條件,可讓您在參考中建構複雜的布林邏輯,以允許到達相同解決方案的多個路徑。

若要建立邏輯註釋,請在任何注釋上使用 Python 的位元邏輯運算子:

>>> a1 & a2, a1 | a2, a1 ^ a2
(pybryt.AndAnnotation, pybryt.OrAnnotation, pybryt.XorAnnotation)

若要建立涉及兩個以上註釋的條件,您可以鏈結運算子,或直接使用這些註釋的子註釋具現化註釋。 與時態性註釋類似,可以藉由更新註釋物件上的對應屬性來設定邏輯註釋的選項。

a3 = pybryt.Value(3)

all_anns = a1 & a2 & a3
all_anns.success_message = "Found a1, a2, and a3"
all_anns.failure_message = "Did not find a1, a2, and a3"

any_anns = a1 | a2 | a3
any_anns.success_message = "Found a1, a2, or a3"
any_anns.failure_message = "Did not find a1, a2, or a3"

one_ann = a1 ^ a2 ^ a3
one_ann.success_message = "Found exactly of a1, a2, or a3"
one_ann.failure_message = "Did not find exactly one of a1, a2, or a3"

PyBryt 也支援 not (~) 運算子,以產生只有在符合其子註釋時才會滿足的註釋。 例如,如果他們的記憶體使用量中有一個不應該存在的特定值,這些註釋可以用來向學生傳送訊息:

not_lst = ~pybryt.Value(lst)
not_lst.failure_message = "Found an incorrect value in your submission; " + \
    "please double-check your implementation."

如果上述註釋在記憶體使用量中找到值 lst,則會提供訊息給學生。 在 pybryt.Value 建構函式中設定 success_message,即可達到相同的效果。

檢定您的知識

1.

下列哪一個運算式會建立註釋判斷提示,指出 a1a2 已滿足?

2.

下列哪一個呼叫會建立註釋,判斷 v2嚴格排列在 v1 之後。 也就是說,v1v2 的時間戳記不能相同?

3.

假設我們使用下列項目建立註釋 ann

a1, a2, a3 = pybryt.Value(1), pybryt.Value(2), pybryt.Value(3)
ann = (a1 | a2) ^ a3

如果轉換成記憶體使用量,下列哪一組會滿足 ann