Как присвоить.lines Seq переменной и выполнить итерацию по ней?

Назначение итератора переменным изменениям, очевидно, как Seq ведет себя. Например

use v6;

my $i = '/etc/lsb-release'.IO.lines;
say $i.WHAT;
say '/etc/lsb-release'.IO.lines.WHAT;
.say for $i;
.say for '/etc/lsb-release'.IO.lines;

результаты в:

(Seq)
(Seq)
(DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS")
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS"

Таким образом, после назначения я получаю только строковое представление последовательности. Я знаю, что могу использовать .say for $i.lines чтобы получить тот же вывод, но я не понимаю разницу между назначенным и неназначенным итератором /Seq.

2 ответа

Решение

Задание в Perl 6 - это всегда вкладывать что-то в другое.

Назначение в Scalar ($ символ) хранит назначаемую вещь в Scalar контейнерный объект, то есть он будет рассматриваться как один элемент; вот почему for $item { } не будет делать итерацию. Есть различные способы преодолеть это; самый концептуально простой способ заключается в использовании <> постфиксный оператор, который удаляет любой Scalar контейнер:

my $i = '/etc/lsb-release'.IO.lines;
.say for $i<>;

Также есть оператор скольжения ("flatten into"), который достигнет того же:

my $i = '/etc/lsb-release'.IO.lines;
.say for |$i;

Назначение в Array будет - если правая часть не помечена как ленивая - итерировать его и сохранить каждый элемент в Array, Таким образом:

my @i = '/etc/lsb-release'.IO.lines;
.say for @i;

Будет работать, но он будет с нетерпением читать все строки в @i до начала цикла. Это нормально для небольшого файла, но менее идеален для большого файла, где мы могли бы предпочесть работать лениво (то есть, только вытягивая часть файла в память за раз). Можно попробовать:

my @i = lazy '/etc/lsb-release'.IO.lines;
.say for @i;

Но это не поможет с проблемой удержания; это просто означает, что массив будет лениво заполняться из файла во время итерации. Конечно, иногда мы могли бы хотеть пройти строки несколько раз, в этом случае назначение в Array будет лучшим выбором.

Напротив, объявление символа и привязка его к этому:

my \i = '/etc/lsb-release'.IO.lines;
.say for i;

Это вообще не операция по вводу в эксплуатацию; это просто делает символ i относиться именно к тому, что lines возвращается. Это гораздо понятнее, чем положить его в Scalar контейнер только чтобы вынуть его снова. Это также немного легче для читателя, так как my \foo = ... никогда не может быть восстановлен, и поэтому читателю не нужно искать какие-либо потенциальные изменения позже в коде.

В заключение, стоит знать, что my \foo = ... Форма на самом деле является обязательным, а не присваиванием. Perl 6 позволяет нам писать это с = оператор, а не принуждение :=, даже если в этом случае семантика := семантика. Это только один из нескольких случаев, когда объявление с инициализатором немного отличается от обычного присваивания, например has $!foo = rand фактически выполняет присваивание для каждого экземпляра объекта, в то время как state $foo = rand запускается только в том случае, если мы находимся на первом входе в текущий клон замыкания.

Если вы хотите иметь возможность перебирать последовательность, вам нужно либо назначить ее позиционеру:

my @i = '/etc/lsb-release'.IO.lines; .say for @i;

Или вы можете сказать итератору, что хотите считать данную вещь итеративной:

.say for @$i

Или вы можете вставить его в список для итератора:

.say for |$i

Другие вопросы по тегам