Курс языка программирования Raku / Функциональное, конкурентное, реактивное и веб-программирование / Функциональное программирование / Итераторы

Написание итератора

Итератор — это просто объект, который выполняет встроенную роль Iterator и предоставляет метод pull-one. Этот метод возвращает следующее значение или IterationEnd, когда ничего не осталось. Вот итератор обратного отсчёта, выдающий число и шагающий к нулю:

class Countdown does Iterator {
    has Int $.n is rw;

    method pull-one {
        return IterationEnd if $!n <= 0;
        return $!n--;
    }
}

Каждый вызов pull-one сначала проверяет, не кончился ли счёт; если нет — возвращает текущее число и уменьшает его. Постфиксное уменьшение $!n-- возвращает значение до вычитания, поэтому числа выходят как 3, 2, 1. Управление им вручную выглядит как и у любого другого итератора:

my $c = Countdown.new(n => 3);
say $c.pull-one; # 3
say $c.pull-one; # 2
say $c.pull-one; # 1
say $c.pull-one =:= IterationEnd; # True

Сам по себе итератор — не то, что цикл for может взять напрямую: циклу нужен итерируемый объект. Заверните итератор в Seq, и по нему станет можно проходить циклом:

for Seq.new(Countdown.new(n => 3)) -> $x {
    say $x;
}

Это напечатает 3, 2, 1. В повседневном коде вы взяли бы gather / take — форма генератора для того же результата куда короче. Но написать роль Iterator напрямую стоит хотя бы раз, потому что это обнажает то, на чём построены gather, map и всякий цикл for: объект, отвечающий на один-единственный вопрос — «каково следующее значение?».

Навигация по курсу

Тест — Протокол итератора   |   Сумма с помощью pull-one


💪 Или перейдите сразу к упражнению этого раздела.