Как запустить функцию без предоставления необязательного параметра [duplicate]

Поведение Object obj(args...) и Object{args...} зависит от конструкторов, определенных в Object.

Возьмем следующий пример:

#include <iostream>
#include <initializer_list>

struct A
{
   A(int a, int b) {std::cout << "Came to A::A()\n";}
};

struct B
{
   B(int a, int b) {std::cout << "Came to B::B(int, int)\n";}
   B(std::initializer_list<int> in) {std::cout << "Came to B::B(std::initializer_list<int>)\n";}
};

int main()
{
   A a1(10, 20); // Resolves to A(int, int)
   A a2{10, 20}; // Resolves to A(int, int)
   A a3{30};     // Does not resolve to anything. It's a compiler error.

   B b1(10, 20); // Resolves to B(int, int)
   B b2{10, 20}; // Resolves to B(std::initializer_list<int> )
   B b3{30};     // Resolves to B(std::initializer_list<int> )

}
123
задан SimonG 6 February 2015 в 17:23
поделиться

7 ответов

Вы также можете использовать missing() для проверки наличия или отсутствия аргумента y:

fooBar <- function(x,y){
    if(missing(y)) {
        x
    } else {
        x + y
    }
}

fooBar(3,1.5)
# [1] 4.5
fooBar(3)
# [1] 3
80
ответ дан Josh O'Brien 22 August 2018 в 12:37
поделиться
  • 1
    Мне нравится пропадать лучше. особенно если у вас много значений по умолчанию NULL, у вас не будет x = NULL, y = NULL, z = NULL в документации к вашему пакету – rawr 6 February 2015 в 17:29
  • 2
    @rawr missing() также более выразителен в том смысле, что он «говорит, что это значит». Плюс это позволяет пользователям передавать значение NULL, в тех местах, где это имеет смысл! – Josh O'Brien 6 February 2015 в 17:32
  • 3
    Для меня есть недостаток в использовании пропавших без вести: при сглаживании аргументов функции вы больше не можете видеть, какие аргументы необходимы, а какие - параметры. – hadley 7 February 2015 в 14:41
  • 4
    @param x numeric; something something; @param y numeric; **optional** something something; @param z logical; **optional** something something – rawr 7 February 2015 в 19:21
  • 5
    missing() ужасен, когда вы хотите передавать аргументы из одной функции в другую. – John Smith 15 September 2016 в 20:15

Есть несколько вариантов, и ни один из них не является официальным правильным способом, и ни один из них не является действительно неправильным, хотя они могут передавать различную информацию на компьютер и другим, читающим ваш код.

Для данного Например, я думаю, что самым ясным вариантом будет предоставление значения по умолчанию для идентификации, в этом случае сделайте что-то вроде:

fooBar <- function(x, y=0) {
  x + y
}

. Это самый короткий из показанных до сих пор параметров, а краткость может помочь в удобочитаемости (а иногда даже скорость выполнения). Понятно, что возвращаемое является суммой x и y, и вы можете видеть, что y не получает значение, которое будет 0, которое при добавлении x будет просто результатом x. Очевидно, что если используется что-то более сложное, чем добавление, тогда потребуется другое значение идентификатора (если оно существует).

Мне очень нравится в этом подходе, так как ясно, что значение по умолчанию используется при использовании функцию args или даже посмотреть файл справки (вам не нужно прокручивать вниз до деталей, это прямо там в использовании).

Недостатком этого метода является то, что значение по умолчанию является сложным (требуется несколько строк кода), то это, вероятно, уменьшит читаемость, чтобы попытаться поместить все это в значение по умолчанию, а подходы missing или NULL станут намного более разумными.

Некоторые другие отличия между методами будут отображаться при передаче параметра другой функции или при использовании функций match.call или sys.call.

Итак, я думаю, что «правильный» метод зависит о том, что вы планируете делать с этим конкретным аргументом и какую информацию вы хотите передать читателям вашего кода.

7
ответ дан Greg Snow 22 August 2018 в 12:37
поделиться

Это мои эмпирические правила:

Если значения по умолчанию можно вычислить из других параметров, используйте выражения по умолчанию, как в:

fun <- function(x,levels=levels(x)){
    blah blah blah
}

, если в противном случае использовать отсутствующие

fun <- function(x,levels){
    if(missing(levels)){
        [calculate levels here]
    }
    blah blah blah
}

В редком случае, когда вы хотите указать значение по умолчанию, которое длится весь сеанс R, используйте getOption

fun <- function(x,y=getOption('fun.y','initialDefault')){# or getOption('pkg.fun.y',defaultValue)
    blah blah blah
}

. Если некоторые параметры применяются в зависимости от класс первого аргумента, используйте S3 generic:

fun <- function(...)
    UseMethod(...)


fun.character <- function(x,y,z){# y and z only apply when x is character
   blah blah blah 
}

fun.numeric <- function(x,a,b){# a and b only apply when x is numeric
   blah blah blah 
}

fun.default <- function(x,m,n){# otherwise arguments m and n apply
   blah blah blah 
}

Используйте ... только при передаче дополнительных параметров на другую функцию

cat0 <- function(...)
    cat(...,sep = '')

Наконец, если вы выбираете использование ..., не передавая точки на другую функцию, предупреждайте пользователя о том, что ваша функция игнорирует любые неиспользуемые параметры, поскольку это может быть very запутанным иначе:

fun <- (x,...){
    params <- list(...)
    optionalParamNames <- letters
    unusedParams <- setdiff(names(params),optionalParamNames)
    if(length(unusedParams))
        stop('unused parameters',paste(unusedParams,collapse = ', '))
   blah blah blah 
}
20
ответ дан Jthorpe 22 August 2018 в 12:37
поделиться
  • 1
    опция метода s3 была одной из первых вещей, которые мне приходило в голову, тоже – rawr 6 February 2015 в 18:22
  • 2
    Оглядываясь назад, я полюбил метод OP, который присваивает NULL в сигнатуре функции, поскольку удобнее делать функции, которые chain красиво. – Jthorpe 25 January 2016 в 23:22

Честно говоря, мне нравится первый способ OP начать его с помощью значения NULL, а затем проверить его с помощью is.null (в первую очередь потому, что это очень просто и легко понять). Возможно, это зависит от того, как люди привыкли к кодированию, но Хэдли, похоже, поддерживает и путь is.null:

Из книги Хэдли «Advanced-R» Глава 6, Функции, стр.84 (для здесь ):

Вы можете определить, был ли аргумент предоставлен или нет с функцией missing ().

i <- function(a, b) {
  c(missing(a), missing(b))
}
i()
#> [1] TRUE TRUE
i(a = 1)
#> [1] FALSE  TRUE
i(b = 2)
#> [1]  TRUE FALSE
i(1, 2)
#> [1] FALSE FALSE

Иногда вы хотите добавить нетривиальное значение по умолчанию, которое может занять несколько строк кода. Вместо того, чтобы вставлять этот код в определение функции, вы можете использовать missing () для условного вычисления, если это необходимо. Однако это затрудняет понимание того, какие аргументы необходимы и которые являются необязательными, без тщательного изучения документации. Вместо этого я обычно устанавливаю значение по умолчанию NULL и использую is.null (), чтобы проверить, был ли предоставлен аргумент.

39
ответ дан LyzandeR 22 August 2018 в 12:37
поделиться
  • 1
    Интересно. Это звучит разумно, но вы когда-нибудь недоумеваете, какие аргументы функции нужны и какие являются необязательными? Я не уверен, что у меня ever действительно был этот опыт ... – Josh O'Brien 6 February 2015 в 17:47
  • 2
    @ JoshO'Brien Я думаю, что у меня не было этой проблемы с любым стилем кодирования, если честно, по крайней мере, это никогда не было серьезной проблемой, вероятно, из-за документации или чтения исходного кода. И именно поэтому я прежде всего говорю, что это действительно вопрос стиля кодирования, к которому вы привыкли. Я довольно долго использовал путь NULL, и, вероятно, именно поэтому я больше привык к нему, когда вижу исходные коды. Мне кажется более естественным. Тем не менее, как вы говорите, база R принимает оба подхода так, это действительно сводится к индивидуальным предпочтениям. – LyzandeR 6 February 2015 в 17:55
  • 3
    К настоящему моменту мне действительно хотелось бы отметить два ответа как правильные, потому что я действительно пришел к использованию как is.null, так и missing в зависимости от контекста и для чего используется аргумент. – SimonG 5 November 2015 в 11:20
  • 4
    Это нормально @SimonG и спасибо :). Я согласен, что оба ответа очень хороши, и иногда они зависят от контекста. Это очень хороший вопрос, и я считаю, что ответы дают очень хорошую информацию и знания, которые в любом случае являются главной целью. – LyzandeR 5 November 2015 в 11:56

Я бы предпочел использовать NULL для ясности того, что требуется и что необязательно. Одно слово предупреждения об использовании значений по умолчанию, которые зависят от других аргументов, как это предлагает Джторп. Значение не задается при вызове функции, но когда аргумент сначала ссылается! Например:

foo <- function(x,y=length(x)){
    x <- x[1:10]
    print(y)
}
foo(1:20) 
#[1] 10

С другой стороны, если вы ссылаетесь на y перед изменением x:

foo <- function(x,y=length(x)){
    print(y)
    x <- x[1:10]
}
foo(1:20) 
#[1] 20

Это немного опасно, потому что это затрудняет отслеживание о том, что инициализируется «y», как если бы она не вызывалась раньше в функции.

6
ответ дан Michael Grosskopf 22 August 2018 в 12:37
поделиться

Просто хотелось указать, что встроенная функция sink имеет хорошие примеры различных способов установки аргументов в функции:

> sink
function (file = NULL, append = FALSE, type = c("output", "message"),
    split = FALSE)
{
    type <- match.arg(type)
    if (type == "message") {
        if (is.null(file))
            file <- stderr()
        else if (!inherits(file, "connection") || !isOpen(file))
            stop("'file' must be NULL or an already open connection")
        if (split)
            stop("cannot split the message connection")
        .Internal(sink(file, FALSE, TRUE, FALSE))
    }
    else {
        closeOnExit <- FALSE
        if (is.null(file))
            file <- -1L
        else if (is.character(file)) {
            file <- file(file, ifelse(append, "a", "w"))
            closeOnExit <- TRUE
        }
        else if (!inherits(file, "connection"))
            stop("'file' must be NULL, a connection or a character string")
        .Internal(sink(file, closeOnExit, FALSE, split))
    }
}
3
ответ дан user5359531 22 August 2018 в 12:37
поделиться
1
ответ дан Keyu Nie 5 November 2018 в 10:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: