289. SymmetricEqualityComparer

פעם שעברה ראינו איך ניתן לחשב את כל הטופולוגיות מעל קבוצה סופית.

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

1
2
3
4
5
6
7
8
var pairs =
from first in candidate
from second in candidate
select new {first, second};
return pairs.All(x =>
candidate.Contains(x.first.Union(x.second), comparer) &&
candidate.Contains(x.first.Intersect(x.second), comparer));

מה שקורה זה שאנחנו עוברים על כל הזוגות האפשריים מטופולוגיה נתונה.

זה די מיותר מאחר ופעולות האיחוד וחיתוך הן קומוטטיביות (חילופיות) – זה לא משנה הסדר בו נבצע את הפעולה, תמיד נקבל את אותה תוצאה.

יש עוד מקרים בהם אנחנו רוצים לעבור על כל הזוגות, כשלא אכפת לנו מהסדר שלהם. את זה אפשר לפתור ע"י כתיבת Comparer שמתייחס אל שני האיברים כזהים:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SymmetricEqualityComparer<TSource> : IEqualityComparer<KeyValuePair<TSource, TSource>>
{
public bool Equals(KeyValuePair<TSource, TSource> x, KeyValuePair<TSource, TSource> y)
{
return(EqualityComparer<TSource>.Default.Equals(x.Key, y.Key) &&
EqualityComparer<TSource>.Default.Equals(x.Value, y.Value)) ||
(EqualityComparer<TSource>.Default.Equals(x.Key, y.Value) &&
EqualityComparer<TSource>.Default.Equals(x.Value, y.Key));
}
public int GetHashCode(KeyValuePair<TSource, TSource> obj)
{
return EqualityComparer<TSource>.Default.GetHashCode(obj.Key) ^
EqualityComparer<TSource>.Default.GetHashCode(obj.Value);
}
}

מה שקורה כאן זה שאנחנו מפרקים את האובייקט שלנו לשני חלקים. אנחנו בודקים ששני האובייקטים שווים לפי החלקים בהתאמה, או לפי החלקים בסדר הפוך.

בGetHashCode אנחנו מחזירים את הXOR של שני החלקים של אובייקט. (זו פעולה קומוטטיבית)

שימו לב שכתבתי את זה ספציפית על KeyValuePair<TSource, TSource> במקום על אובייקט יותר גנרי, אבל אפשר להרחיב את זה ע"י קבלת Delegateים שמפרקים את האובייקטים לחלקים (כאן אלה יהיו Delegate שמחזיר את הKey וDelegate שמחזיר את הValue)

נוכל להשתמש בזה בדוגמא של אתמול כך:

1
2
3
4
5
6
7
8
9
10
11
12
SymmetricEqualityComparer<IEnumerable<T>> symmetricComparer =
new SymmetricEqualityComparer<IEnumerable<T>>();
var pairs =
(from first in candidate
from second in candidate
select newKeyValuePair<IEnumerable<T>,IEnumerable<T>>(first, second))
.Distinct(symmetricComparer);
return pairs.All(x =>
candidate.Contains(x.Key.Union(x.Value), comparer) &&
candidate.Contains(x.Key.Intersect(x.Value), comparer));

כך אנחנו עוברים על הזוגות בלי חשיבות לסדר.

המשך יום בלי חשיבות לסדר טוב!

שתף