Назначить Seq (Seq) в массивы

Каков правильный синтаксис для назначения Seq (Seq) в несколько типизированных массивов без предварительного назначения Seq скаляру? Seq должен быть как-то сплющен? Это не удается:

class A { has Int $.r }

my A (@ra1, @ra2);

#create two arrays with 5 random numbers below a certain limit

#Fails: Type check failed in assignment to @ra1; expected A but got Seq($((A.new(r => 3), A.n...)
(@ra1, @ra2) =
   <10 20>.map( -> $up_limit {
        (^5).map({A.new( r => (^$up_limit).pick ) })
    });

Ответ 1

Не совсем уверен, что это так, но похоже, что обе ваши последовательности сохраняются в @ra1, а @ra2 остается пустым. Это нарушает ограничение типа.

Что работает

@ra1, @ra2 Z= <10 20>.map(...);

Ответ 2

TL; DR Binding быстрее, чем назначение, поэтому, возможно, это лучшее решение вашей проблемы:

:(@ra1, @ra2) := <10 20>.map(...);

Назначение/копирование

Упрощенно, ваш нерабочий код:

(@listvar1, @listvar2) = list1, list2;

В P6 infix = означает присвоение/копирование списка значений справа от = в одну или несколько контейнерных переменных слева от =.

Если переменная слева связана с Scalar контейнером, то она примет один элемент. Затем процесс копирования начинает нацеливаться на следующую переменную контейнера.

Если переменная слева связана с контейнером Array, то она примет все оставшиеся значения. Таким образом, ваша первая переменная массива получает и list1 и list2. Это не то, что вы хотите.

Упрощение, вот ответ Кристофа:

@listvar1, @listvar2 Z= list1, list2;

Отложив = на мгновение, Z является инфиксной версией процедуры zip. Это похоже на (физический почтовый индекс, соединяющий последовательные аргументы слева и справа. При использовании с оператором он применяет этот оператор к паре. Таким образом, вы можете прочитать выше Z= как:

@listvar1 = list1;
@listvar2 = list2;

Работа выполнена.

Но назначение в контейнеры Array влечет за собой:

  • Индивидуальное копирование столько отдельных элементов списка, сколько есть в контейнерах. (В коде вашего примера list1 и list2 содержат по 5 элементов каждый, так что всего будет 10 операций копирования.)

  • Принудительное изменение размеров контейнеров по мере необходимости для размещения предметов.

  • Удвоение памяти, используемой элементами (исходные элементы списка и дубликаты, скопированные в элементы Array).

  • Проверка того, что тип каждого элемента соответствует ограничению типа элемента.

Назначение обычно намного медленнее и требует больше памяти, чем привязка...

переплет

:(@listvar1, @listvar2) := list1, list2;

Оператор := связывает аргументы справа от всего, что слева.

Если слева одна переменная, то все особенно просто. После связывания переменная теперь относится именно к тому, что справа. (Это особенно просто и быстро - быстрая проверка типа и все готово.)

Но это не так в нашем случае.

Привязка также принимает автономный литерал подписи слева. Символ :(...) в моем ответе является отдельным литералом Signature.

(Сигнатуры обычно присоединяются к подпрограмме без префикса двоеточия. Например, в sub foo (@var1, @var2) {} (@var1, @var2) представляет собой подпись, прикрепленную к подпрограмме foo. Но, как вы можете видите, можно написать сигнатуру отдельно и дать P6 знать ее как сигнатуру, поставив перед парой двоеточий префикс. Главное отличие в том, что любые переменные, перечисленные в сигнатуре, должны быть уже объявлены.)

Когда слева находится литеральный сигнатур, то привязка происходит в соответствии с той же логикой, что и при привязке аргументов в обычных вызовах к получающей обычной подписи. (Действительно, он использует тот же путь кода в компиляторе.)

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

sub foo (@listvar1, @listvar2) { }
foo list1, list2;

то есть эффект такой же как:

@listvar1 := list1;
@listvar2 := list2;

Опять же, как и с ответом Кристофа, работа выполнена.

Но на этот раз мы избежали большинства накладных расходов, описанных в конце предыдущего раздела.