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

时间:2018-12-12 23:13:27

标签: optional-parameters keyword-argument chapel

在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
}

但是,我想知道是否会有更优雅的(?)或惯用的(...)方法...

编辑

正如评论中所建议的,重载几个函数以获取不同的接口也很方便,但是我想在某个时候我需要将一些“虚拟”对象传递给最终(成熟的)例程并询问后者查看传递的对象是否为“虚拟” ... 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,这是我的代码的修改版本,使用了_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

1 个答案:

答案 0 :(得分:2)

此答案讨论了3个答案:

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

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

选项1

问题中所述的

proc test( x: real, arr: [] int = empty1Dint, use_arr = false )策略是合理的,如果有些冗长。这里的主要缺点是,如果您不希望呼叫站点必须通过testuse_arr=true,则需要use_arr=false的更多重载。这是一个执行此操作的简单程序:

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

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

如果未提供optional参数,它将使用类型为optional的{​​{1}}实例化。这是不传递某些东西的一种方式。从技术上讲,这与检查是否提供了默认值并不相同,但是我认为像void这样的呼叫站点在传达test(optional=_void)的值时应该忽略(因为它是{{1} }。

反正这里是代码:

optional