126. Tuple

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

לפעמים אנחנו בכלל רוצים לעבוד עם שלשות/רביעיות וכלה בnיות (הכינוי המתמטי לאוסף סדור של n איברים, באנגלית נקרא Tuple)

עד לFramework 4.0 היינו יכולים לעשות מניפולציה כזאת:

1
2
3
4
5
6
7
8
9
public static KeyValuePair<int, int> FindMaxAndMin(IEnumerable<int> numbers)
{
int max;
int min;
// TODO: Find the max and min of the set
return new KeyValuePair<int, int>(min, max);
}

(תעזבו האם נכון שתהיה פונקציה כזאת או לא)

כלומר לעשות שימוש מעוות בKeyValuePair כדי לשמור שני ערכים – אלא שאין לנו באמת מפתח וערך כאן.

(למי שלא מכיר את KeyValuePair, ראו גם טיפ 17)

דוגמא נוספת היא כזאת: נניח שאנחנו מעוניינים למצוא את כל השלשות של מספרים שלמים (a,b,c) בתחום מסוים כך שמתקיים השוויון

$ a^2+ b^2 = c^2 $

(לשלשות כאלה קוראים שלשות פיתגוריות)

כאן יהיה לנו יותר קשה להשתמש בKeyValuePair, כיוון שאנחנו מעוניינים לשמור שלשות.

בFramework 4.0 קיים פתרון יותר אלגנטי – קיימת מחלקה בשם Tuple המייצגת n-יה. למען הדיוק, קיימות מספר מחלקות גנריות כאלה, מאיבר יחיד עד שמינייה:

1
2
3
4
5
6
7
8
public class Tuple<T1>
public class Tuple<T1, T2>
public class Tuple<T1, T2, T3>
public class Tuple<T1, T2, T3, T4>
public class Tuple<T1, T2, T3, T4, T5>
public class Tuple<T1, T2, T3, T4, T5, T6>
public class Tuple<T1, T2, T3, T4, T5, T6, T7>
public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

קיימות שתי דרכים להשתמש בזה:

הראשונה היא באופן דומה למה שראינו KeyValuePair:

1
2
3
4
5
6
7
8
9
public static Tuple<int, int> FindMaxAndMin(IEnumerable<int> numbers)
{
int max;
int min;
// TODO: Find the max and min of the set
return new Tuple<int, int>(min, max);
}

וגישה תתבצע כך:

1
2
3
Tuple<int, int> minAndMax = FindMaxAndMin(numbers);
Console.WriteLine("Min is {0}", minAndMax.Item1);
Console.WriteLine("Max is {0}", minAndMax.Item2);

קיימת דרך אחרת לאתחל, והיא באמצעות הoverloadים של הפונקציה הסטטית Tuple.Create:

1
2
3
4
5
6
7
8
public static Tuple<int, int> FindMaxAndMin(IEnumerable<int> numbers)
{
int max;
int min;
// TODO: Find the max and min of the set
return Tuple.Create<int, int>(min, max);
}

למי שזוכר את טיפ מספר 28, אפשר לתת לקומפיילר לזהות לבד את הטיפוסים הגנריים:

1
2
3
4
5
6
7
8
public static Tuple<int, int> FindMaxAndMin(IEnumerable<int> numbers)
{
int max;
int min;
// TODO: Find the max and min of the set
return Tuple.Create(min, max);
}

כך שיש פה יתרון בכתיבה 😃.

בדוגמה של שלשות פיתגוריות:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static IEnumerable<Tuple<int, int, int>> PythagoreanTriplets(int range)
{
for (int a = 0; a < range; a++)
{
for (int b = 0; b < range; b++)
{
for (int c = 0; c < range; c++)
{
if (a * a + b * b == c * c)
{
yield return Tuple.Create(a, b, c);
}
}
}
}
}

ואם אנחנו רוצים יותר מ8 איברים? חשבו על זה ומציעים לנו את הפתרון הבא:

1
2
3
4
5
6
7
var tenNumbers =
new Tuple<int, int, int, int, int, int, int, Tuple<int, int,int>>
(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10));
Console.WriteLine(tenNumbers.Item6); // 6
Console.WriteLine(tenNumbers.Rest.Item2); // 9

האמת שלא הכי אלגנטי, אבל עשוי להתאים.

שבוע טיזרים טוב

שתף