可能没有默认值的可选函数参数?

Optional function arguments with no default value possible?

在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" 对象传递给最终(成熟的)例程并要求后者查看传递的对象是否为 "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

虽然上述方法适用于数组,但规则需要根据所用对象的类型进行更改。所以,我想知道是否有一些系统的方法,例如,传递一个 "null pointer" 或一些独特的全局对象来告诉最终例程实际数据的存在。 (但是,如上所述,上述方法适用于数组)。

编辑 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,这是我的代码的修改版本,使用 _voidvoid:

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 个答案:

  1. 问题编辑中讨论的策略。
  2. 使用Box类型的策略
  3. 使用具有 void 默认值的泛型函数的策略

这些选项中我最喜欢的是选项 3。

选项 1

proc test( x: real, arr: [] int = empty1Dint, use_arr = false )问题中描述的策略是合理的,如果有点冗长。这里的主要缺点是,如果您不希望调用站点必须传递 use_arr=trueuse_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

另一种方法是创建一个 class 来存储可选参数数据,并默认传递 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

我最喜欢的这个问题的解决方案完全是第三种策略。 礼拜堂包括 a value of void type called _void。关键是声明proc test( optional:?t=_void )。这里 test 是一个通用函数 - 语法 argument:?t 表示参数可以有不同的类型(在函数中可以作为 t 使用)。这是获得也具有默认值的通用参数所必需的(否则参数将仅具有从默认值推断出的类型)。

如果没有提供 optional 参数,它将使用具有类型 voidoptional 进行实例化。这是一种不通过某些东西的方式。从技术上讲,这与检查是否提供了默认值不同,但我认为像 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]);