Как я могу передать последовательность в качестве параметра в Perl 6?
В Perl 6 я могу повторить буквальную последовательность:
.say for 0 ... 3;
Я могу привязать к скаляру и повторить это:
my $s := 0 ... 3;
.say for $s;
Но я не могу привязать скаляр, передать его в качестве аргумента и затем повторить это:
my $t := 0 ... 3;
show( $t );
sub show ( $seq ) { .say for $seq }
Подпрограмма получает один элемент типа Seq, но не выполняет итерацию.
0
1
2
3
0
1
2
3
(0 1 2 3)
Что-то в процессе подготовки параметра уже проходит через все?
2 ответа
Значение, хранящееся в скалярном контейнере, не повторяется автоматически
Хотя оба $s
а также $seq
являются скалярами (иначе называемыми "переменными"), $s
напрямую связан со значением Seq, тогда как ваш $seq
связан с посредником Скаляр (обратите внимание, заглавные S
) "контейнер", который в свою очередь содержит Seq. Значение, хранящееся в скалярном контейнере, не повторяется автоматически при использовании с такими функциями, как for
,
Более подробно:
my $s := 0 ... 3;
.say for $s;
Поскольку my
объявление переменной использует оператор прямой привязки :=
для инициализации, $s
напрямую связан с одним значением Seq 0 ... 3
,
Это означает, что for
оператор видит одно значение Seq, определяет, что он выполняет роль Iterable, и выравнивает (повторяет) его.
Теперь рассмотрим это:
my $s := 0 ... 3;
my $container = $s;
.say for $container;
Потому что второй my
декларация использует оператор присваивания =
для инициализации новая переменная $container
сначала привязывается к новому скалярному контейнеру, который затем "содержит" все, что ему назначено.
В соответствии с общепринятыми соглашениями Слёрпи (в частности: "Итерируемое внутри скалярного контейнера не учитывается"), for
оператор не выполняет итерацию значения, хранящегося в скалярном контейнере, поэтому .say for $container
линия только один say
,
Аналогичная ситуация относится к вашему оригиналу show
рутина, потому что объявления параметров переменных (семантически) являются контейнерами по умолчанию.
Один из вариантов - вместо этого добавить is raw
черта к вашему $seq
параметр:
sub show ( $seq is raw ) { .say for $seq }
Это предотвращает обычное автоматическое связывание $seq
в скалярный контейнер (который, в свою очередь, будет содержать значение Seq) как часть вызова show
,
Другой вариант, чтобы позволить $seq
быть привязанным к скалярному контейнеру, но явно сгладить (итерировать) $seq
переменная в теле show
рутина с использованием префикса |
:
sub show ( $seq ) { .say for |$seq }
Вместо is raw
Параметр trait, предложенный raiph, вы также можете использовать переменную без сигилов в качестве параметра, который не вводит Scalar
контейнер:
sub show (\seq) { .say for seq }
(Вы также можете использовать эту форму для нормальных переменных, как в my \a = 5; say a;
, но имейте в виду, что они предназначены только для одного назначения.)
Разновидностью этого является +
форма, которая передает необработанный аргумент, если это Iterable
(как List
или же Seq
), но когда передается не повторяемый аргумент, он переносится в List
одного элемента, так что тело функции может полагаться на всегда получать Iterable
:
sub show (+seq) { .say for seq }
(Это то, что большинство встроенных процедур обработки списка, как grep
а также zip
использовать.)
Конечно, если вы предпочитаете использовать $
параметр, который вводит Scalar
Контейнер, вы можете просто "разгрузить" его снова перед итерацией по нему, вызвав .list
метод на это:
sub show ($seq) { .say for $seq.list } # explicit
sub show ($seq) { .say for @$seq } # short-hand syntax
(Обновление: Эх, .list
на самом деле превращает Seq
в List
т.е. это не будет эффективным для памяти в случае большого Seq
, С помощью |$seq
как вы уже обнаружили в своем ответе, не имеет этой проблемы.)
Из документов...
sub foo($bar) { say $bar } # $bar is a parameter
foo(42); # 42 is an argument
Также здесь...
Sigil Binds to Default behavior
$ Scalar Generate new Scalar, use instead of Scalar in argument, if any
@ Positional Bind directly to the argument
@ PositionalBindFailover If binding failed, call argument's .cache method, bind to result
% Associative Bind directly to the argument
& Callable Bind directly to the argument
\ (anything) Bind directly to the argument, keep existing Scalar, if any
С помощью Sigil $ скалярный контейнер вызывающего объекта отличается от скалярного контейнера вызываемого объекта, и вызов выполняет неявное присвоение. [Тем не менее '\' и 'is raw' - это способы отключить это и принудительно привязать, если это то, что вы предпочитаете.]
Я так понимаю, что намерение состоит в том, чтобы по умолчанию контейнеры были локальными для блоков, чтобы помочь разделению и распараллеливанию памяти. Все остальные сигилы связываются напрямую - но они поддаются распараллеливанию другими способами.
Таким образом, Sigil $ имеет значение как для итеративности, так и для разделения.