在Chapel中,我们可以很容易地设置函数形式参数的默认值,例如
proc test( a = 1, b = 2.0, c = "hi" ) {
...
}
并使用关键字调用函数:
test( 10 ); // a = 10, b = 2.0, c = "hi"
test( b = 3.14 ); // a = 1, b = 3.14, c = "hi"
test( c = "yo" ); // a = 1, b = 2.0, c = "yo"
在这里,我想知道是否可以定义一个不需要预定义默认值的关键字参数。更具体地说,我想写一个函数,它可以根据情况选择性地接收数组(例如,保存中间数据(。在这里,唯一的要求是我可以检查是否传递了实际的参数,并且不需要给出默认的数组值。我想象过类似的东西
proc test( ..., optional d: [] real ) {
if present( d ) then ...;
}
or
proc test( ..., d: [] real = None ) {
if present( d ) then ...;
}
但找不到类似的东西。目前,我的解决方法是给出一些伪默认值,并检查它们的属性,以确定是否传递了实际的参数。
proc test( arr = empty2Dreal ) { ... } // where "empty2Dreal" is a pre-defined global array
or
proc test( arr = reshape( [0.0], {1..1,1..1} ) ) { ... } // some dummy array
}
然而,我想知道是否有更优雅(?(或更地道(?(的方法。。。
编辑
正如评论中所建议的,重载几个函数以获得不同的接口也很方便,但在某个时候,我想我需要将一些"dummy"对象传递给最终(fully(例程,并让后者看看传递的对象是否是"dummy-"。。。MWE是这样的:
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int )
{
writeln("test() with 2 args");
writeln(( x, arr ));
// here, I need to check whether the passed object is
// an actual array or not by some predefined rule
if arr.size > 0 then writeln("got a non-empty array");
}
proc test( x: real )
{
writeln("test() with 1 arg");
test( x = x, arr = empty1Dint );
}
var work = [1,2,3,4,5];
test( x = 1.0 );
writeln();
test( x = 1.0, arr = work );
它给出
test() with 1 arg
test() with 2 args
(1.0, )
test() with 2 args
(1.0, 1 2 3 4 5)
got a non-empty array
相应的默认值版本为
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int = empty1Dint )
{
writeln("test() with 2 args");
writeln(( x, arr ));
if arr.size > 0 then writeln("got a non-empty array");
}
var work = [1,2,3,4,5];
test( x = 1.0 );
writeln();
test( x = 1.0, arr = work );
它给出
test() with 2 args
(1.0, )
test() with 2 args
(1.0, 1 2 3 4 5)
got a non-empty array
尽管上述方法适用于数组,但规则需要根据所使用的对象类型进行更改。因此,我想知道是否有一些系统的方法,例如,传递一个"空指针"或一些唯一的全局对象来告诉最终例程实际数据的存在。(但是,如上所述,上述方法适用于数组(。
编辑2
另一种方法可能只是简单地传递一个额外的标志来使用传递的数组(然后不需要太多考虑默认对象的性质,所以总体上可能更简单…(
const empty1Dint: [1..0] int;
proc test( x: real, arr: [] int = empty1Dint, use_arr = false )
{
writeln( "x= ", x );
if use_arr {
writeln("working with the passed array...");
for i in 1..arr.size do arr[ i ] = i * 10;
}
}
test( x = 1.0 );
writeln();
var work: [1..5] int;
test( x = 2.0, arr = work, use_arr = true );
writeln( "work = ", work );
编辑3
根据答案中的选项3,这里是我使用_void
和void
:的代码的修改版本
proc test( x: real, arr: ?T = _void )
{
writeln( "ntest():" );
writeln( "x = ", x );
writeln( "arr = ", arr );
writeln( "arr.type = ", arr.type:string );
writeln( "T = ", T:string );
if arr.type != void {
writeln( "doing some checks" );
assert( isArray( arr ) );
}
if arr.type != void {
writeln( "writing arr" );
for i in 1..arr.size do arr[ i ] = i * 10;
}
}
// no optional arg
test( x = 1.0 );
// use an optional arg
var work: [1..5] int;
test( x = 2.0, arr = work );
writeln( "nmain> work = ", work );
结果:
test():
x = 1.0
arr =
arr.type = void
T = void
test():
x = 2.0
arr = 0 0 0 0 0
arr.type = [domain(1,int(64),false)] int(64)
T = [domain(1,int(64),false)] int(64)
doing some checks
writing arr
main> work = 10 20 30 40 50
这个答案讨论了3个答案:
- 问题编辑中讨论的策略
- 使用
Box
类型的策略 - 使用具有void默认值的泛型函数的策略
这些选项中我最喜欢的是选项3。
选项1
问题中描述的proc test( x: real, arr: [] int = empty1Dint, use_arr = false )
策略是合理的,尽管有点冗长。这里的主要缺点是,如果您不希望呼叫站点必须通过use_arr=true
或use_arr=false
,则需要更多的test
过载。这里有一个简单的程序可以做到这一点:
proc test(optional, hasOptional:bool) {
writeln("in test");
writeln(" optional is ", optional);
if hasOptional == false then
writeln(" note: default was used for optional");
}
proc test(optional) {
test(optional, hasOptional=true);
}
proc test() {
var emptyArray:[1..0] int;
test(emptyArray, hasOptional=false);
}
test();
test([1, 2, 3]);
选项2
另一种选择是创建一个类来存储可选的参数数据,并在默认情况下传递nil。
class Box {
var contents;
}
proc makeArray() {
var A:[1..2] int;
return A;
}
proc emptyBox() {
var A:[1..0] int;
var ret: owned Box(A.type) = nil;
return ret;
}
proc test( optional=emptyBox() ) {
writeln("in test with optional=", optional);
}
test();
test(new owned Box(makeArray()));
这里最棘手的部分是makeArray((和emptyBox((返回的数组类型必须匹配。使用类型别名使它们引用相同的数组类型是可能的,但具体如何适应取决于您的应用程序。这种方法的另一个问题是,它会导致在传递此类参数的过程中复制数组。而且,人们必须考虑Box
将在哪里被摧毁。test
是挂起数组值(例如,将其存储在数据结构中(,还是只是临时使用它?在我的示例中,这是由emptyBox
返回的类型设置的。
标准库获得这样的Box
类型可能是合理的,但现在还没有。
选项3
对于这个问题,我最喜欢的解决方案是第三种策略。Chapel包含一个名为_void的void类型的值。关键是声明proc test( optional:?t=_void )
。这里test
是一个通用函数——语法argument:?t
表示参数可以具有不同的类型(在函数中可用作t
(。这对于获得一个也具有默认值的泛型参数是必要的(否则,该参数将仅具有从默认值推断出的类型(。
如果没有提供optional
参数,它将使用类型为void
的optional
进行实例化。作为一种不通过的方式,这是有道理的。从技术上讲,这与检查是否提供了默认值不同,但我认为像test(optional=_void)
这样的呼叫站点在通信时相当清楚,应该忽略optional
的值(因为它是void
(。
无论如何,这是代码:
proc test( optional:?t=_void ) {
writeln("in test");
writeln(" optional is ", optional);
if optional.type == void then
writeln(" note: default was used for optional");
}
test();
test([1, 2, 3]);