Pull to refresh

Реализация итераторов в C# (часть 2)

Reading time2 min
Views11K
Original author: Raymond Chen
Реализация итераторов в C# (часть 1).

Теперь, когда вы имеете в своём багаже общее представление о том, что стоит за итераторами, вы уже можете ответить на некоторые вопросы их использования. Вот сценарий, основанный на реальных событиях:

У меня есть большой и сложный итератор, и я хочу отрефакторить его. Для большей наглядности условимся, что перечислитель считает от 1 до 100 дважды. (Разумеется, в реальной жизни итератор не будет столь простым.)
IEnumerable<int> CountTo100Twice()
{
    int i;
    for (i = 1; i <= 100; i++) {
        yield return i;
    }
    for (i = 1; i <= 100; i++) {
        yield return i;
    }
}

Как мы знаем из Pragramming 101, мы можем вынести общий код в подпрограмму и вызвать её. Но когда я так делаю, я получаю ошибку компилятора:
IEnumerable<int> CountTo100Twice()
{
    CountTo100();
    CountTo100();
}
 
void CountTo100()
{
    int i;
    for (i = 1; i <= 100; i++) {
        yield return i;
    }
}

Что я делаю не так? Как мне перенести “подсчёт до 100” в подпрограмму и дважды вызвать её из функции CountTo100Twice?

Как мы только что увидели, итераторы это вовсе не подпрограммы. Представленная выше техника прекрасно работала бы, если бы мы строили итераторы из, скажем, волокон вместо конечных автоматов. Но раз уж используются автоматы, yield return выражения должны располагаться на “верхнем уровне”. Так как вы будете итерировать с помощью подпрограмм?

Вы делаете саму подпрограмму итератором, а затем “вытягиваете” результаты из главной функции:
IEnumerable<int> CountTo100Twice()
{
    foreach (int i in CountTo100()) yield return i;
    foreach (int i in CountTo100()) yield return i;
}
 
IEnumerable<int> CountTo100()
{
    for (i = 1; i <= 100; i++) {
        yield return i;
    }
}

Упражнение: Рассмотрим следующий фрагмент:
foreach (int i in CountTo100Twice()) {
    // ...
}

Объясните, что произойдёт на 150-ом (стопятидесятом) вызове MoveNext() в цикле выше. Обдумайте последствие этого для рекурсивных алгоритмов (таких как обход деревьев).
Tags:
Hubs:
Total votes 22: ↑17 and ↓5+12
Comments3

Articles