Как я могу передать последовательность в качестве параметра в 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 $ имеет значение как для итеративности, так и для разделения.

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