Два ключевых слова — 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?
Это можно сделать через каналы.