42. Understanding anonymous delegates

פעם שעברה ראינו מה זה delegate אנונימי.

הרבה אנשים עשויים לחשוב (ולעתים, בצדק) שמדובר בסה”כ בקיצור מרושל לכך שלא היה למתכנת כוח לכתוב מתודה שתעשה בשבילו את העבודה.

אני אדגים עכשיו משהו שקל לעשות באמצעות anonymous delegates, ויותר מסובך לעשות בלי.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static IEnumerable<string> BinaryStrings()
{
List<string> result =
new List<string>();
for (int i5 = 0; i5 <= 1; i5++)
{
for (int i4 = 0; i4 <= 1; i4++)
{
for (int i3 = 0; i3 <= 1; i3++)
{
for (int i2 = 0; i2 <= 1; i2++)
{
for (int i1 = 0; i1 <= 1; i1++)
{
result.Add(string.Format("{0}{1}{2}{3}{4}",
i1,
i2,
i3,
i4,
i5));
}
}
}
}
}
return result;
}

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

אז מה הבעיה?

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

נצטרך ליצור מספר לולאות מקוננות בגודל המספר שקיבלנו.

בעייתי קצת, לא?

שתי אופציות קלאסיות לפתור את הבעיה שאני מעלה עכשיו, הן הבאות. הראשונה היא להשתמש בייצוג הבינארי של המספר ואז לרוץ עד $ 2^n $. (פתרון איטרטיבי)

השנייה היא להשתמש ברקורסיה.

אני אראה דרך שלישית באמצעות anonymous delegates:

ניצור delegate כזה

1
public delegate IEnumerable<string> Concat(IEnumerable<string> elements);

כעת ניצור לו מימוש שמשרשר את $ 0,1 $ רק פעם אחת:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static IEnumerable<string> ConcatOnce
(IEnumerable<string> elements)
{
List<string> result = new List<string>();
for (int i = 0; i <= 1; i++)
{
foreach (string element in elements)
{
result.Add(element + i);
}
}
return result;
}

זה משרשר את $ 0 $ ו$ 1 $ לכל איבר שקיבלנו.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static Concat BinaryStrings(int n)
{
Concat concat = ConcatOnce;
for (int i = 1; i < n; i++)
{
Concat current = concat;
concat =
delegate(IEnumerable<string> elements)
{
return ConcatOnce(current(elements));
};
}
return concat;
}

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

1
ConcatOnce(ConcatOnce(ConcatOnce(...)));

כאשר השרשור הוא $ n$ פעמים.

כעת נוכל לקרוא לה כך:

1
2
3
4
5
6
7
8
9
10
11
Concat concat = BinaryStrings(4);
IEnumerable<string> binaryStrings =
concat(new string[] {""});
// Same as binaryStrings =
// ConcatOnce(ConcatOnce(ConcatOnce(ConcatOnce(new string[]{""}))));
foreach (string binaryString in binaryStrings)
{
Console.WriteLine(binaryString);
}

המשך יום אנונימי טוב

שתף