Курс Raku / Функційне, конкурентне, реактивне та веб-програмування / Функційне програмування / Ітератори

Протокол ітератора

Кожне значення, яке можна обійти циклом — список, діапазон, масив, — може вручити вам ітератор через метод .iterator. Ітератор має один суттєвий метод, pull-one, що щоразу повертає наступне значення:

my $it = (10, 20, 30).iterator;
say $it.pull-one; # 10
say $it.pull-one; # 20
say $it.pull-one; # 30

Коли значення вичерпуються, pull-one повертає замість справжнього елемента особливий вартовий об’єкт, IterationEnd. Запит іще одного значення в того самого $it це показує:

say $it.pull-one =:= IterationEnd; # True

Оператор =:= перевіряє, чи це той самий об’єкт, і саме так розпізнають вартового. Ця деталь важлива всередині циклу, де вам потрібно і зберегти витягнуте значення, і перевірити його. Якщо ви присвоїте його через =, значення потрапить у контейнер, і порівняння дивитиметься на контейнер, а не на IterationEnd. Ліки — зв’язати через :=, щоб змінна просто була тим, що повернув pull-one:

my $it = <a b c>.iterator;
loop {
    my $v := $it.pull-one;      # зв'язування, а не присвоєння
    last if $v =:= IterationEnd;
    say $v;
}

Цикл друкує a, b, c і зупиняється. Саме це під капотом робить за вас цикл for: він викликає .iterator на тому, що ви обходите, і далі викликає pull-one, аж поки не зустріне IterationEnd. Вам рідко доводиться виписувати це вручну, але зробивши так одного разу, ви бачите, що Raku насправді обходить циклом ітератори, а не списки.

Практика

Виконайте 1 тест за змістом цієї теми.

Навігація курсом

Ітератори   |   Тест — Протокол ітератора


💪 Або перейдіть одразу до вправи цього розділу.