Chapel 中的函数变量和函数数组
Function variable and an array of functions in Chapel
在下面的代码中,我试图通过将函数名称视为通常的变量来创建一个 "function pointer" 和一个函数数组:
proc myfunc1() { return 100; }
proc myfunc2() { return 200; }
// a function variable?
var myfunc = myfunc1;
writeln( myfunc() );
myfunc = myfunc2;
writeln( myfunc() );
// an array of functions?
var myfuncs: [1..2] myfunc1.type;
writeln( myfuncs.type: string );
myfuncs[ 1 ] = myfunc1;
myfuncs[ 2 ] = myfunc2;
for fun in myfuncs do
writeln( fun() );
这似乎按预期工作(使用 Chapel v1.16)
100
200
[domain(1,int(64),false)] chpl__fcf_type_void_int64_t
100
200
所以我想知道上面函数变量的用法是否合法?为了创建一个函数数组,通常是先定义一个具有所需签名的具体函数,然后像上面的例子那样引用它的类型(.type
)吗?
此外,将此类变量视为"usual"变量是否没有问题,例如将它们作为参数传递给其他函数或将它们包含在class/record的字段中? (如果后面的问题太宽泛,请忽略它们......)如果有任何潜在的陷阱(如果有的话),我将不胜感激。
此代码使用第一个 class 功能支持,这是 Chapel 语言设计中的 prototype/draft。您可以在 First-class Functions in Chapel 技术说明中阅读有关原型支持的更多信息。
虽然 first-class 函数的许多用途在 1.16 及更高版本中有效,但您可以预期将重新审视这方面的语言设计。特别是目前对于是否可以捕获变量的问题没有合理的答案(现在尝试这样做可能会导致令人困惑的错误)。不过,我不知道这会在哪个未来版本中改变。
关于 myfunc1.type
部分,我提到的技术说明中称为 "Specifying the type of a first-class function" 的部分提出了另一种策略。但是我没有发现在这种情况下使用 myfunc1.type
有任何问题。
最后,请注意当前编译器中的 lambda
支持实际上是通过使用 this
方法创建 class 来运行的。所以你可以做同样的事情 - 创建一个 "function object" (借用 C++ 术语) - 具有相同的效果。 "function object" 可以是一条记录或 class。如果它是 class,您可以使用继承来创建一个对象数组,这些对象可以根据其动态类型响应相同的方法。此策略可能使您能够解决第一个 class 函数的当前问题。即使完成了 first-class 函数支持,"function object" 方法也允许您更明确地了解捕获的变量。特别是,您可以将它们作为字段存储在 class 中,并在 class 初始化程序中设置它们。下面是创建和使用不同类型函数对象数组的示例:
class BaseHandler {
// consider these as "pure virtual" functions
proc name():string { halt("base name called"); }
proc this(arg:int) { halt("base greet called"); }
}
class HelloHandler : BaseHandler {
proc name():string { return "hello"; }
proc this(arg:int) { writeln("Hello ", arg); }
}
class CiaoHandler : BaseHandler {
proc name():string { return "ciao"; }
proc this(arg:int) { writeln("Ciao ", arg); }
}
proc test() {
// create an array of handlers
var handlers:[1..0] BaseHandler;
handlers.push_back(new HelloHandler());
handlers.push_back(new CiaoHandler());
for h in handlers {
h(1); // calls 'this' method in instance
}
}
test();
是的,在您的示例中,您使用 Chapel 的 initial support for first-class functions. To your second question, you could alternatively use a function type helper 来声明函数数组:
var myfuncs: [1..2] func(int);
这些 first-class 函数对象可以作为参数传递给函数——这就是 Futures.async()
works – or stored as fields in a record (Try It Online! example). Chapel's first-class function capabilities also include lambda functions.
需要说明的是,此支持的 "initial" 方面附带警告(来自文档):
This mechanism should be considered a stopgap technology until we have developed and implemented a more robust story, which is why it's being described in this README rather than the language specification.
在下面的代码中,我试图通过将函数名称视为通常的变量来创建一个 "function pointer" 和一个函数数组:
proc myfunc1() { return 100; }
proc myfunc2() { return 200; }
// a function variable?
var myfunc = myfunc1;
writeln( myfunc() );
myfunc = myfunc2;
writeln( myfunc() );
// an array of functions?
var myfuncs: [1..2] myfunc1.type;
writeln( myfuncs.type: string );
myfuncs[ 1 ] = myfunc1;
myfuncs[ 2 ] = myfunc2;
for fun in myfuncs do
writeln( fun() );
这似乎按预期工作(使用 Chapel v1.16)
100
200
[domain(1,int(64),false)] chpl__fcf_type_void_int64_t
100
200
所以我想知道上面函数变量的用法是否合法?为了创建一个函数数组,通常是先定义一个具有所需签名的具体函数,然后像上面的例子那样引用它的类型(.type
)吗?
此外,将此类变量视为"usual"变量是否没有问题,例如将它们作为参数传递给其他函数或将它们包含在class/record的字段中? (如果后面的问题太宽泛,请忽略它们......)如果有任何潜在的陷阱(如果有的话),我将不胜感激。
此代码使用第一个 class 功能支持,这是 Chapel 语言设计中的 prototype/draft。您可以在 First-class Functions in Chapel 技术说明中阅读有关原型支持的更多信息。
虽然 first-class 函数的许多用途在 1.16 及更高版本中有效,但您可以预期将重新审视这方面的语言设计。特别是目前对于是否可以捕获变量的问题没有合理的答案(现在尝试这样做可能会导致令人困惑的错误)。不过,我不知道这会在哪个未来版本中改变。
关于 myfunc1.type
部分,我提到的技术说明中称为 "Specifying the type of a first-class function" 的部分提出了另一种策略。但是我没有发现在这种情况下使用 myfunc1.type
有任何问题。
最后,请注意当前编译器中的 lambda
支持实际上是通过使用 this
方法创建 class 来运行的。所以你可以做同样的事情 - 创建一个 "function object" (借用 C++ 术语) - 具有相同的效果。 "function object" 可以是一条记录或 class。如果它是 class,您可以使用继承来创建一个对象数组,这些对象可以根据其动态类型响应相同的方法。此策略可能使您能够解决第一个 class 函数的当前问题。即使完成了 first-class 函数支持,"function object" 方法也允许您更明确地了解捕获的变量。特别是,您可以将它们作为字段存储在 class 中,并在 class 初始化程序中设置它们。下面是创建和使用不同类型函数对象数组的示例:
class BaseHandler {
// consider these as "pure virtual" functions
proc name():string { halt("base name called"); }
proc this(arg:int) { halt("base greet called"); }
}
class HelloHandler : BaseHandler {
proc name():string { return "hello"; }
proc this(arg:int) { writeln("Hello ", arg); }
}
class CiaoHandler : BaseHandler {
proc name():string { return "ciao"; }
proc this(arg:int) { writeln("Ciao ", arg); }
}
proc test() {
// create an array of handlers
var handlers:[1..0] BaseHandler;
handlers.push_back(new HelloHandler());
handlers.push_back(new CiaoHandler());
for h in handlers {
h(1); // calls 'this' method in instance
}
}
test();
是的,在您的示例中,您使用 Chapel 的 initial support for first-class functions. To your second question, you could alternatively use a function type helper 来声明函数数组:
var myfuncs: [1..2] func(int);
这些 first-class 函数对象可以作为参数传递给函数——这就是 Futures.async()
works – or stored as fields in a record (Try It Online! example). Chapel's first-class function capabilities also include lambda functions.
需要说明的是,此支持的 "initial" 方面附带警告(来自文档):
This mechanism should be considered a stopgap technology until we have developed and implemented a more robust story, which is why it's being described in this README rather than the language specification.