Объявить и инициализировать типизированный массив из диапазона

Недавно пробовал my Array @a = 'a'..'z'; и my Array @a = @('a'..'z');.

Оба выдают следующую ошибку:

Type check failed in assignment to @a; expected Array but got Str ("a")
in block <unit> at <unknown file> line 1

Однако инициализация без типа работает и, похоже, в конечном итоге создает массив:

> my @a = 'a'..'z';
> @a.^name
Array

Почему это так?


person Jessica Nowak    schedule 16.05.2019    source источник
comment
Я предполагаю, что когда вы печатаете массив, тип применяется к каждому элементу в массиве. Итак, в вашем случае тип каждого элемента не Array, а Str. Вот почему вы получаете ошибку expected Array but got Str, когда вводите @a как my Array @a.   -  person Håkon Hægland    schedule 16.05.2019


Ответы (4)


TL;DR Я даю относительно простой ответ в разделе Почему это так? Однако это объяснение может быть неадекватным1, поэтому я рассматриваю некоторые альтернативы. в Объявить и инициализировать типизированный массив из диапазона.

Почему это так?

  • my @a; объявляет новый Array (инициализируется пустым) и "привязывает" его к символу @a . Таким образом, my @a; say @a.^name возвращает Array. Нет необходимости использовать слово Array в объявлении или инициализации массива — достаточно @.2

  • my @a = 'a'..'z' пытается скопировать каждое значение в диапазоне от 'a' до 'z', по одному, в @a[0], @a[1] и т. д. Новый массив, привязанный к @a, имеет ограничение типа для каждый из его элементов (поясняется в следующем разделе); он будет проверен для каждого значения (и будет успешным).

  • my Array @a объявляет Array с ограничением типа Array для его элементов (так что это массив массивов). my Array @a; say @a.^name возвращает Array[Array], чтобы указать это. my Array @a = 'a'..'z'; завершается ошибкой при копировании первого значения ("a"), потому что это значение Str, а не Array.

Объявить и инициализировать типизированный массив из диапазона

my @a = 'a'..'z';

Часть my @a этого оператора объявляет переменную, которая привязана (ссылается) к новому массиву типа Array. Поскольку ограничение типа элемента не указано, элементы нового массива должны соответствовать Mu, Наиболеесамый ненепредполагаемый тип в P6. Другими словами, это пустой массив, готовый содержать любые значения, которые вы хотите поместить в него. (Можно сказать, что say @a.^name отображает Array, а не Array[Mu], потому что [Mu] считается самымсамым неинтересным.)

... = 'a'..'z' инициализирует новый массив. Инициализация не влияет на уже установленные ограничения типа массива. Он просто доставляет копии строк 'a', 'b' и т. д. в массив (который автоматически расширяется, чтобы получить их в @a[0], @a[1] и т. д.).

Я рекомендую разработчикам избегать добавления явных ограничений типа для переменных и явного приведения значений, если они не уверены, что это желательно. (см. мои замечания в скобках в конце более раннего ответа SO.) Тем не менее, вы можете сделать это:

my Str @a = 'a'..'z';      # `Array` elements constrained to `Str`
my Str @a = (0..99)>>.Str; # Coerce value to match constraint

Кроме того, P6 поддерживает явное связывание, а не назначение значения или списка значений. Самый распространенный способ сделать это — использовать := вместо =:

my @a := 'a'..'z'; say @a.WHAT; say @a[25]; # (Range)␤z

Обратите внимание, что явная привязка @a означает, что @a не было привязано к новому Array, а вместо этого к значению Range. А поскольку Range может вести себя как Positional, позиционная индексация по-прежнему работает.

Следующие утверждения были бы чрезмерно избыточной явной типизацией, но оба они работали бы и давали точно такой же результат, как и друг друга, хотя первое было бы быстрее:

my Str @a := Array[Str].new('a'..'z'); 
my Str @a  = Array[Str].new('a'..'z'); 

Есть еще что обсудить по этой теме, но, возможно, вышеизложенного достаточно для этого вопроса/ответа. Если нет, пожалуйста, задавайте дополнительные вопросы в комментариях под исходным вопросом и/или ниже.

Сноски

1 Более ранняя версия этого ответа начиналась с:

my Array @a ...
# My array of thoughts raised by this declaration
# and your questing "why?" in this SO question
# began with wry thoughts about complicated answers
# about reasons your array is awry and pedances

(Я придумал слово "педанс" для обозначения чего-то, что кажется педантичным, но хорошо звучит при правильном использовании - что произойдет естественным образом, как только вы познакомитесь с его явно идиосинкразической, но на самом деле полезной природой. , Что еще более важно, мне нужно было что-то, что рифмуется с "ответами".)

2 Вот несколько мнемоник для значения @ в P6:

  • Это выглядит как нулевая цифра (0) с ???? (Математический Курсив Мелкий A) внутри него -- и переменная @foo по умолчанию является индексированной 0 ???????????????????? (или @????????????????).

  • Оно похоже на слово "at". Массив содержит элементы в индексах.

person raiph    schedule 16.05.2019

Установите тип элемента в массиве:

my Str @a = 'a'..'z'; 
say @a; #[a b c d e f g

Чтобы увидеть, какой это тип, вы можете использовать .WHAT

my Str @a = 'a'..'z'; 
say @a.WHAT #(Array[Str])

Чтобы проверить, является ли это массивом, вы можете выполнить smartmatch

my Str @a = 'a'..'z'; 
say 'is array' if @a ~~ Array; #is array

say 'is str array' if @a ~~ Array[Str]; #is str array

say 'is str array' if @a ~~ Array[Int]; #
person Valle Lukas    schedule 16.05.2019

@('a'..'z') сделает List, а не Array. Изменение его на ['a'..'z'] даст Array.

Но это все равно даст вам ошибку типа при назначении его my Array @a, как вы это сделали.

Если вы хотите присвоить весь массив элементу другого массива, вам нужно как-то его перечислить, например:

$[1,2,3]; #Still an array but treated as single item
[1,2,3], ; #Note the list operator (comma), this give you a list of lists

Итак, в вашем случае:

'a'..'z'; - это диапазон, его необходимо преобразовать в массив, поэтому

Диапазон ['a'..'z']; теперь оценивается как массив

Диапазон $['a'..'z']; теперь оценивается как массив и принимается как один элемент.

my Array @a=$['a'..'z'];

say @a;

#OUTPUT:
#[[a b c d e f g h i j k l m n o p q r s t u v w x y z]]
# Which is an array of arrays;

Не уверен, что это то, что вам нужно, но он удаляет ошибку типа.

person drclaw    schedule 16.05.2019

Когда вы объявляете массив, вы можете легко указать, к какому типу относятся элементы массива.

my Str @a = 'a'..'z';

Вы также можете указать точный класс, который используется для хранилища.
Следующая строка точно такая же, как и строка выше; поскольку Array является классом хранения по умолчанию.

my @a is Array[Str] = 'a'..'z';

Вы даже можете комбинировать их.
Это тоже самое.

my Str @a is Array = 'a'..'z';

Когда вы написали следующую строку.

my Array @a = 'a'..'z';

То, что вы на самом деле говорили, было:

my @a is Array[Array] = 'a'..'z';

Я полагаю, вы думали, что пишете это.

my @a is Array = 'a'..'z';

Это может быть полезно, если вы не хотите, чтобы элементы изменялись.

my @a is List = 'a'..'z';

Или если у вас есть особые потребности, которые не удовлетворяет класс Array по умолчанию.

use Array::Unique; # doesn't really exist yet (afaik)

my @a is Array::Unique[Str] = 'a'..'z';

my Str @b is Array::Unique  = 'a'..'z';
person Brad Gilbert    schedule 31.05.2019