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

问题描述 投票:1回答:1

在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
optional-parameters keyword-argument chapel
1个回答
2
投票

这个答案讨论了3个答案:

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

我最喜欢的选项是选项3。

Option 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]);

Option 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类型可能是合理的,但它现在没有。

Option 3

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

如果没有提供optional参数,它将使用optional类型的void实例化。这是一种不通过某种东西的方法。从技术上讲,它与检查是否提供了默认值不同,但我认为像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]);
© www.soinside.com 2019 - 2024. All rights reserved.