54. yield return

ראינו בפעמים הקודמות מהו IEnumerable ולמה הוא משמש.

נניח שאנחנו רוצים לייצר סדרה חשבונית. דרך אחת לעשות זאת היא לממש IEnumerable משלנו.

אבל מה שזה אומר, זה לממש IEnumerator משלנו מפני שכל מה שיש בIEnumerable זו פונקציה של GetEnumerator.

אבל בIEnumerator יש 3 פונקציות שאנחנו צריכים לממש, ואנחנו צריכים לכתוב 2 מחלקות שלמות משלנו רק בשביל ליצור IEnumerable משלנו.

בשביל לפתור את הבעיה הזאת, המציאו את הkeyword האדיר yield return בC# 2.0.

הדבר מאפשר לנו לכתוב Enumerableים משלנו בקלות יחסית.

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

נוכל לכתוב את זה ככה:

1
2
3
4
5
6
7
public static IEnumerable<int> Range(int from, int to)
{
for (int i = from; i <= to; i++)
{
yield return i;
}
}

שימו לב שyield return לא בדיוק יוצא מהפונקציה כמו return. מה שקורה בעצם זה שכל הstate של הפונקציה (כל המשתנים הלוקאליים, הcallstack והשאר), נשמרים באיזשהו מקום, ובכל פעם שאנחנו עושים MoveNext על הEnumerator, אנחנו חוזרים לפונקציה עם אותו הstate הקודם!!!

דוגמה לשימוש:

1
2
3
4
5
6
IEnumerable<int> numbers = Range(100, 200);
foreach (int number in numbers)
{
// ...
}

ייתכן וחלקכם חושבים עכשיו, "יופי, יכולתי גם ליצור מערך שמכיל את המספרים מ100 עד 200".

אוקיי, אבל האם יכולתם להחזיר מערך שמכיל את כל המספרים הטבעיים?

בדרך זו אפשר!

1
2
3
4
5
6
7
public static IEnumerable<int> NaturalNumbers()
{
for (int i= 1; ; i++)
{
yield return i;
}
}

עוד דוגמאות:

למשל, יצירת סדרה חשבונית באורך סופי:

1
2
3
4
5
6
7
8
9
10
11
public static IEnumerable<int> ArithmeticSequence
(int first, int difference, int count)
{
int currentTerm = first;
for (int i = 0; i < count; i++)
{
yield return currentTerm;
currentTerm += difference;
}
}

או באורך אינסופי:

1
2
3
4
5
6
7
8
9
10
11
public static IEnumerable<int> ArithmeticSequence
(int first, int difference)
{
int currentTerm = first;
for (int i = 1; ; i++)
{
yield return currentTerm;
currentTerm += difference;
}
}

יצירת סדרת פיבונאצ’י באורך סופי:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static IEnumerable<int> Fibbonacci(int count)
{
int current = 1;
int previous = 0;
for (int i = 0; i < count; i++)
{
yield return current;
int temp = current;
current = current + previous;
previous = temp;
}
}

או באורך אינסופי:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static IEnumerable<int> Fibbonacci()
{
int current = 1;
int previous = 0;
for (int i = 0; ; i++)
{
yield return current;
int temp = current;
current = current + previous;
previous = temp;
}
}

וככה נוכל למשל למצוא את המספר הראשון בסדרת פיבונאצ’י שמתחלק ב23:

1
2
3
4
5
6
7
8
9
10
IEnumerable<int> fibbonacciNumbers = Fibbonacci();
foreach (int number in fibbonacciNumbers)
{
if (number % 23 == 0)
{
Console.WriteLine(number);
break;
}
}

התשובה היא 46368 ד"א.

בכל אופן מדובר בכלי חזק ביותר.

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

שתף