2016-11-21 8 views
7

В 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) 

Что-то в процессе подготовки параметра повторяется через все уже?

ответ

10

Значение удерживается в Scalar контейнере не автоматически итерироваться

В то время как $s и $seq являются scalars (так называемый «переменные»), $s связан непосредственно к значению Seq, тогда как ваши $seq привязан к посредник Scalar (примечание в верхнем регистре S) «контейнер», который, в свою очередь, содержит Seq. Значение, хранящееся в контейнере Scalar, равно , а не, автоматически повторяется при использовании с такими функциями, как 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 сначала привязываются к новому скалярному контейнеру, который затем «содержит» все, что получает назначение.

В соответствии с языком шириной Slurpy Conventions (в частности: «итератор внутри скалярного контейнера не считается»), А 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 } 
6

Вместо признак is raw параметра предложил 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 контейнера, вы можете просто «decontainerize» он снова перед перебором над ним, путем вызова .list метода на него:.

sub show ($seq) { .say for $seq.list } # explicit 

sub show ($seq) { .say for @$seq }  # short-hand syntax 

(Обновление: Eh, .list фактически превращает Seq в List, то есть он не будет экономить память при большом Seq. Использование |$seq, как вы уже обнаружили в своем собственном ответе, не имеет этой проблемы.)

+0

Спасибо! Но да, я думаю о бесконечных ленивых последовательностях. :) –