Необязательные аргументы функции без значения по умолчанию возможно? - PullRequest
0 голосов
/ 13 декабря 2018

В 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

Хотя вышеуказанный подход работает для массивов, правило необходимо изменитьв зависимости от типа используемых объектов.Итак, мне было интересно, существует ли какой-то систематический способ, например, передать «нулевой указатель» или какой-то уникальный глобальный объект, чтобы сообщить конечной процедуре о наличии фактических данных.(Но, как отмечалось выше, вышеуказанный подход работает для массивов).

Edit 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

1 Ответ

0 голосов
/ 13 декабря 2018

В этом ответе обсуждаются 3 ответа:

  1. Стратегия, обсуждаемая при редактировании вопроса.
  2. Стратегия, использующая тип Box
  3. Стратегияиспользование универсальной функции со значением по умолчанию void

Мой любимый из этих вариантов - вариант 3.

Вариант 1

proc test( x: real, arr: [] int = empty1Dint, use_arr = false ) Стратегия, описанная в вопросе,разумно, если немного многословно.Основным недостатком здесь является то, что вам потребуется больше перегрузок test, если вы не хотите, чтобы сайты вызовов проходили use_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

Мое любимое решение этой проблемы - третья стратегия.Часовня включает в себя значение типа void, называемое _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]);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...