Объявите и инициализируйте типизированный массив из диапазона - PullRequest
14 голосов
/ 16 мая 2019

Я недавно попробовал 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

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

Ответы [ 4 ]

12 голосов
/ 16 мая 2019

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, M ost u типом присвоения в P6. Другими словами, это пустой массив, готовый содержать любые значения, которые вы хотите поместить в него. (Можно сказать, что say @a.^name отображает Array, а не Array[Mu], потому что [Mu] считается M ost u неинтересным.)

... = '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 проиндексировано ????? (или @????).

  • Это звучит как слово "в".Массив имеет элементы с индексами .

7 голосов
/ 16 мая 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;

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

7 голосов
/ 16 мая 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]; #
1 голос
/ 31 мая 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';
...