Два ключевых слова — gather и take — появились чуть ли не в самой ранней версии Perl 6. Давайте посмотрим, как они работают сегодня.
Пару дней назад мы видели, как gather и take коллекционировали промисы от параллельных потоков. Вот более простой пример:
my @data = gather { take 'a'; take 'b'; take 'c'; }; say @data; # [a b c]
Этот пример довольно прозрачен: три вызова take собирают данные в последовательность, которую и возвращает gather.
Обратите внимание, что возвращается именно последовательность (sequence), которая представлена типом Seq. Тип данных всегда можно посмотреть, вызвав на объекте метод WHAT:
(gather { take 'a'; take 'b'; take 'c'; }).WHAT.say; # (Seq)
Действие gather распространяется и на другие take, которые, например, происходят при вызове функций внутри gather. Следующий пример дает об этом представление:
my @a = gather { take 'a'; f('b'); } sub f($x) { take $x; } say @a; # [a b]
lazy gather
Наконец, модификация для ленивых вычислений — блок lazy gather. Немного модифицируем предыдущий пример, чтобы функция сообщала о своем вызове:
my @data = gather { take f('a'); take f('b'); take f('c'); } sub f($x) { say "Taking $x"; return $x; }
Эта программа сразу печатает три строки:
Taking a Taking b Taking c
Если же перед gather поставить lazy, программа завершится, ничего не напечатав.
Однако, код после take будет выполняться по мере того, как мы начнем читать данные из массива:
my @data = lazy gather { take f('a'); take f('b'); take f('c'); } sub f($x) { say "Taking $x"; return $x; } say @data[0];
Программа сначала сообщит о «взятии» первого значения, а потом напечатает его:
Taking a a
Если же попытаться сразу вывести, например, третий элемент (say @data[2]), то сработают все три take:
Taking a Taking b Taking c c
А есть ли возможность запустить gather/take больше чем в «один поток», т.е. чтобы в одном месте программы работал gather1/take1, а в другом gather2/take2?
Это можно сделать через каналы.