来自二维数组切片的意外类型
Unexpected type from a slice of 2-dimensional array
在下面的代码中,我尝试使用 var
或 ref
获取数组 A
的第一行和第二行。在这里,我的理解是 var
总是创建一个新数组,而 ref
为右侧创建一个别名(或引用)。
var A: [1..2, 1..3] int;
A = 0;
var row1 = A[ 1, .. ];
ref row2 = A[ 2, .. ];
row1 = 10;
row2 = 20;
writeln( "row1 = ", row1 );
writeln( "row2 = ", row2 );
writeln( "A = ", A );
结果符合预期(即A
的第一行没有修改):
row1 = 10 10 10
row2 = 20 20 20
A = 0 0 0
20 20 20
但是如果我打印 row1
和 row2
的类型
writeln( "row1.type = ", row1.type: string );
writeln( "row2.type = ", row2.type: string );
我得到这个结果:
row1.type = [ArrayViewRankChangeDom(1,int(64),false,2*bool,2*int(64),ArrayViewRankChangeDist(DefaultDist,2*bool,2*int(64)))] int(64)
row2.type = [ArrayViewRankChangeDom(1,int(64),false,2*bool,2*int(64),ArrayViewRankChangeDist(DefaultDist,2*bool,2*int(64)))] int(64)
这有点令人惊讶,因为我假设 row1
是一个 "usual" 数组(即,不是别名)。例如下面的vec
(我称之为"usual"数组)
var vec: [1..3] int;
writeln( "vec.type = ", vec.type: string );
给予
vec.type = [domain(1,int(64),false)] int(64)
所以我想知道 row1
和 vec
(作为数组类型)之间是否有一些区别...?
I'm wondering whether there is some difference between row1 and vec (as array types)...?
有区别,但您是否认为它是类型上的差异在某种程度上取决于您对类型的概念。每个 Chapel 数组都是根据域映射来定义的,域映射指定了它是如何定义的(以及它的域是如何定义的)。如果未指定域映射,则使用默认域映射。数组的域映射在技术上是其类型的一部分,但在实践中,它往往是一个比可应用于数组的操作更能影响数组实现方式的方面。
因此,在您的示例中,vec
是由 int(64)
索引的 int(64)
元素的一维数组(并且其索引是不可跨越的,这就是 false
在其域的签名中表示),其域映射是默认域映射。同时,您的切片表达式 A[i, ..]
也是由 int(64)
索引的 int(64)
元素的一维数组(并且也是不可跨越的),但它们的域映射是 ArrayViewRankChange
。这是一个内部域映射,表示另一个数组的排名更改别名 ("view")。
[使用这个不同的域图来表示数组的秩变化切片的基本原理的简短版本是这样的:如果原始二维数组是分布式的(比方说),它的一维切片大概应该是也类似地分布——这表明它不应该是默认域映射(未分布),也不应该是原始数组的分布(描述如何实现 2D 事物)。因此,选择是要么让等级变化切片需要域映射来创建自己的新低维域映射(这往往介于具有挑战性和不可能之间),要么使用知道如何从 1D 索引转换的新域映射到原始分布的二维索引。这就是 ArrayViewRankChange
域映射的作用。可以在从幻灯片 27 ("Array Views") 开始的 1.15 release notes 中找到更长版本的基本原理。]
鉴于上述情况,row2
也将 ArrayViewRankChange
指示为其域映射可能是有道理的,因为它是对排名更改切片表达式的引用。但是 row1
可能会更令人惊讶,因为它是一个全新的数组,导致 ArrayViewRankChange
数组的深度复制,因此可以使用传统的默认数组存储。但是在分布式情况下可以再次看到基本原理,其中新数组变量应该(大概)具有与原始变量相同的分布---我们这样做的方法是通过 ArrayViewRankChange
.
如果你想强制数组切片为 "a normal array",你可以使用更精确的声明来实现:
var row1b: [A.domain.dim(2)] A.eltType = A[1, ..];
此时它的类型将匹配 vec
s:
row1b.type = [domain(1,int(64),false)] int(64)
综上所述,希望您会发现可以在大多数可以使用 vec
的地方使用 row1
和 row2
。如果没有,请提出后续问题或在 GitHub.
上提交问题
作为结束语,可以说编译器 could/should 在更干净地打印数组类型方面做得更好,或者至少在打印其类型时提供不同的冗长级别选项(例如,在很多情况下,也许不需要识别域映射,或者域类型可以更简单地打印出来?)
在下面的代码中,我尝试使用 var
或 ref
获取数组 A
的第一行和第二行。在这里,我的理解是 var
总是创建一个新数组,而 ref
为右侧创建一个别名(或引用)。
var A: [1..2, 1..3] int;
A = 0;
var row1 = A[ 1, .. ];
ref row2 = A[ 2, .. ];
row1 = 10;
row2 = 20;
writeln( "row1 = ", row1 );
writeln( "row2 = ", row2 );
writeln( "A = ", A );
结果符合预期(即A
的第一行没有修改):
row1 = 10 10 10
row2 = 20 20 20
A = 0 0 0
20 20 20
但是如果我打印 row1
和 row2
writeln( "row1.type = ", row1.type: string );
writeln( "row2.type = ", row2.type: string );
我得到这个结果:
row1.type = [ArrayViewRankChangeDom(1,int(64),false,2*bool,2*int(64),ArrayViewRankChangeDist(DefaultDist,2*bool,2*int(64)))] int(64)
row2.type = [ArrayViewRankChangeDom(1,int(64),false,2*bool,2*int(64),ArrayViewRankChangeDist(DefaultDist,2*bool,2*int(64)))] int(64)
这有点令人惊讶,因为我假设 row1
是一个 "usual" 数组(即,不是别名)。例如下面的vec
(我称之为"usual"数组)
var vec: [1..3] int;
writeln( "vec.type = ", vec.type: string );
给予
vec.type = [domain(1,int(64),false)] int(64)
所以我想知道 row1
和 vec
(作为数组类型)之间是否有一些区别...?
I'm wondering whether there is some difference between row1 and vec (as array types)...?
有区别,但您是否认为它是类型上的差异在某种程度上取决于您对类型的概念。每个 Chapel 数组都是根据域映射来定义的,域映射指定了它是如何定义的(以及它的域是如何定义的)。如果未指定域映射,则使用默认域映射。数组的域映射在技术上是其类型的一部分,但在实践中,它往往是一个比可应用于数组的操作更能影响数组实现方式的方面。
因此,在您的示例中,vec
是由 int(64)
索引的 int(64)
元素的一维数组(并且其索引是不可跨越的,这就是 false
在其域的签名中表示),其域映射是默认域映射。同时,您的切片表达式 A[i, ..]
也是由 int(64)
索引的 int(64)
元素的一维数组(并且也是不可跨越的),但它们的域映射是 ArrayViewRankChange
。这是一个内部域映射,表示另一个数组的排名更改别名 ("view")。
[使用这个不同的域图来表示数组的秩变化切片的基本原理的简短版本是这样的:如果原始二维数组是分布式的(比方说),它的一维切片大概应该是也类似地分布——这表明它不应该是默认域映射(未分布),也不应该是原始数组的分布(描述如何实现 2D 事物)。因此,选择是要么让等级变化切片需要域映射来创建自己的新低维域映射(这往往介于具有挑战性和不可能之间),要么使用知道如何从 1D 索引转换的新域映射到原始分布的二维索引。这就是 ArrayViewRankChange
域映射的作用。可以在从幻灯片 27 ("Array Views") 开始的 1.15 release notes 中找到更长版本的基本原理。]
鉴于上述情况,row2
也将 ArrayViewRankChange
指示为其域映射可能是有道理的,因为它是对排名更改切片表达式的引用。但是 row1
可能会更令人惊讶,因为它是一个全新的数组,导致 ArrayViewRankChange
数组的深度复制,因此可以使用传统的默认数组存储。但是在分布式情况下可以再次看到基本原理,其中新数组变量应该(大概)具有与原始变量相同的分布---我们这样做的方法是通过 ArrayViewRankChange
.
如果你想强制数组切片为 "a normal array",你可以使用更精确的声明来实现:
var row1b: [A.domain.dim(2)] A.eltType = A[1, ..];
此时它的类型将匹配 vec
s:
row1b.type = [domain(1,int(64),false)] int(64)
综上所述,希望您会发现可以在大多数可以使用 vec
的地方使用 row1
和 row2
。如果没有,请提出后续问题或在 GitHub.
作为结束语,可以说编译器 could/should 在更干净地打印数组类型方面做得更好,或者至少在打印其类型时提供不同的冗长级别选项(例如,在很多情况下,也许不需要识别域映射,或者域类型可以更简单地打印出来?)