我有一个超图数据结构,有两个数组,一个用于边,一个用于顶点(类似于二分图).我有一个调整数组大小的问题,所以我尝试了简化的例子:
ar dom = {0..0};
var arr: [dom] int;
writeln(arr);
dom = {0..#2};
writeln(arr);
dom = {0..#1};
writeln(arr);
record Vertex {}
record Edge {}
record Wrapper {
type nodeType;
type idType;
var id: idType;
}
record NodeData {
type nodeIdType;
var ndom = {0..-1};
var neighborList: [ndom] nodeIdType;
proc numNeighbors() return ndom.numIndices;
var lock$: sync bool = true;
// This method is not parallel-safe
proc addNodes(vals) {
lock$; // acquire lock
neighborList.push_back(vals);
lock$ = true; // release the lock
}
proc readWriteThis(f) {
f <~> new ioLiteral("{ ndom = ") <~> ndom <~> new ioLiteral(", neighborlist = ") <~> neighborList <~> new ioLiteral(", lock$ = ") <~> lock$.readFF() <~> new ioLiteral(" }");
}
}
type vType = Wrapper(Vertex, int);
type eType = Wrapper(Edge, int);
var dom1 = {0..0};
var arr1: [dom1] NodeData(eType);
writeln(arr1);
dom1 = {0..#2};
writeln(arr1);
dom1 = {0..#1};
writeln(arr1);
Run Code Online (Sandbox Code Playgroud)
当我尝试运行此代码时,它会挂起以下输出:
$ ./resize -nl 1
salloc: Granted job allocation 15015
0
0 0
0
{ ndom = {0..-1}, neighborlist = , lock$ = true }
Run Code Online (Sandbox Code Playgroud)
因此,对整数数组进行大小调整非常合适,但是我无法调整我的记录数组.我究竟做错了什么?
作为旁注,当我尝试在完整代码中调整域名时,我看到域名发生了变化,但我使用域名的阵列根本没有变化.至少代码不会挂起.
编辑
我尝试了另一个实际上更能说明原始问题的例子:
class Test {
var dom;
var ints: [dom] int;
proc resize(size) {
dom = {dom.low..size};
}
}
var test = new Test(dom = {0..-1});
writeln(test);
test.resize(1);
writeln(test);
Run Code Online (Sandbox Code Playgroud)
这是我看到的输出:
$ ./resize -nl 1
salloc: Granted job allocation 15038
{dom = {0..-1}, ints = }
{dom = {0..1}, ints = }
salloc: Relinquishing job allocation 15038
Run Code Online (Sandbox Code Playgroud)
所以我的问题是这个resize方法没用.它确实更改了域,但它不会更改成员数组.
以下是针对您在编辑示例中看到的问题的回应:
从 1.17 版本开始,我担心您会陷入编译器的黑暗角落,我很遗憾它的存在,尽管我认为我们可以帮助您摆脱困境。
从一些背景和重要的上下文开始:从一开始,Chapel 就支持类和记录的构造函数(例如,proc C(...)a class C),但这些构造函数的设计很幼稚,特别是关于通用类和记录。在过去的几个版本中,我们已经从构造函数转向初始化程序(例如,proc init(..)对于class C)来解决这些限制。
截至今天发布的版本 1.17,初始化器处于相当好的状态(例如,我现在将它们用于我编写的所有新代码并且很少对它们发誓),但是如果您既不提供初始化器也不提供构造函数(如您的示例中所示) ,编译器将创建一个默认构造函数(而不是默认初始化程序),因此可能会遇到一些长期存在的问题。对于版本 1.18,目标是让编译器默认创建初始值设定项并完全弃用构造函数。
因此,这里有一些方法可以解决编辑中较小的测试程序的问题,所有这些似乎都在 Chapel 的 1.17 版本中为我产生了正确的输出:
1) 减少类的通用性。在这里,我给了该dom字段一个初始值,以便编译器可以确定其类型,这显然有助于它使用默认构造函数,足以生成预期的输出:
class Test {
var dom = {0..-1};
var ints: [dom] int;
proc resize(size) {
dom = {dom.low..size};
}
}
var test = new Test(dom = {0..-1});
writeln(test);
test.resize(1);
writeln(test);
Run Code Online (Sandbox Code Playgroud)
2) 编写显式初始化程序。在这里,我将保留dom通用性,但创建一个初始化程序,将其分配给与您的调用签名相匹配的new:
class Test {
var dom;
var ints: [dom] int;
proc init(dom: domain(1)) {
this.dom = dom;
}
proc resize(size) {
dom = {dom.low..size};
}
}
var test = new Test(dom = {0..-1});
writeln(test);
test.resize(1);
writeln(test);
Run Code Online (Sandbox Code Playgroud)
3)(最后的手段)请求编译器为您创建一个默认的初始值设定项(而不是默认的构造函数)。这种方法实际上并不适合最终用户,不适用于所有情况,并且将来会消失,但同时了解一下可能会很方便。在这里,我将一个编译指示附加到该类,以告诉编译器创建默认初始值设定项而不是默认构造函数。尽管默认情况下编译器不会创建默认初始值设定项,但对于许多类和记录,如果您要求的话,它就可以创建默认初始值设定项,而这恰好是其中之一:
pragma "use default init"
class Test {
var dom;
var ints: [dom] int;
proc resize(size) {
dom = {dom.low..size};
}
}
var test = new Test(dom = {0..-1});
writeln(test);
test.resize(1);
writeln(test);
Run Code Online (Sandbox Code Playgroud)
出于篇幅的考虑,我在这里只讨论了较短的示例,而不是较长的示例,但希望这些技术也能对此有所帮助(如果需要,我很乐意在较长的示例上花费更多时间)。