从范围声明和初始化类型化数组

Declare and initialize a typed array from a range

我最近尝试了 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

为什么会这样?

设置数组中元素的类型:

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]; #

@('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;

不确定它是否是您想要的,但它消除了类型错误。

TL;DR 我在Why is this case?中提供了一个相对简单的答案,但是,这个解释可能不充分1 所以我在声明和初始化范围内的类型化数组

中回顾了一些备选方案

Why is this the case?

  • my @a;声明一个新的Array(初始化为空)并且"binds"它到符号@a。因此 my @a; say @a.^name returns Array。不需要在数组的声明或初始化中使用单词 Array——@ 就足够了。2

  • my @a = 'a'..'z' 尝试 复制 'a''z' 范围内的每个值,一次一个, into @a[0]@a[1] 等。绑定到 @a 的新数组对其每个元素都有类型约束(在下一节中解释) );它将检查每个值(并且会成功)。

  • my Array @a 声明一个 Array ,其元素具有 Array 类型约束(因此它是一个数组数组)。 my Array @a; say @a.^name returns Array[Array] 来表示这一点。 my Array @a = 'a'..'z'; 复制第一个值 ("a") 时失败,因为它是 Str 值而不是 Array.

Declare and initialize a typed array from a range

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

此语句的 my @a 部分声明了一个变量,该变量绑定到(引用)类型为 Array 的新数组。因为没有指定元素类型约束,所以新数组的元素被约束为与Mu一致,Mostunassuming类型在P6。换句话说,它是一个空数组,可以包含您想要放入其中的任何值。 (可以说 say @a.^name 显示 Array 而不是 Array[Mu] 因为 [Mu] 被认为是 Most u 很有趣。)

... = 'a'..'z' 初始化新数组。初始化对数组已经建立的类型约束没有影响。它只是将字符串 'a''b' 的副本传送到 数组(auto-expands 将它们接收到 @a[0]@a[1] 等等)。

我建议开发人员避免在变量上添加显式类型约束和对值进行显式强制转换,除非他们确信它们是可取的。 (请参阅我在 末尾的附加说明。)也就是说,您可以选择这样做:

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'); 

关于此主题还有更多要讨论的内容,但前面的内容可能已足够 question/answer。如果没有,请在下面的原始问题 and/or 下的评论中提出更多问题。

脚注

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

(我编造了 "pedances" 这个词,意思是 看起来 很迂腐,但如果使用得当,它会自然地发生熟悉它看似特殊但实际上有用的性质。更重要的是,我需要一些与 "answers" 押韵的东西。)

2 下面是P6中@的意思的几个助记符:

  • 看起来像 零数字 (0) 带有 </code> (<a href="https://unicode-table.com/en/1D44E/" rel="nofollow noreferrer">Mathematical Italic Small A</a>) 在里面——一个 <code>@foo 变量默认是一个 0 索引 </code> (或 <code>@).

  • 听起来像这个词"at"。一个数组有元素 at indices.

声明数组时,可以方便地指定数组元素的类型。

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

您也可以指定用于存储的确切 class。
下一行与上一行完全相同;因为 Array 是默认存储 class.

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 class。

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';