Как присвоить.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