Передача отсутствующего аргумента из функции в функцию в R

Я узнал, что обычной практикой является использование необязательных аргументов в функции и их проверка с помощью missing () (например, как описано в SO 22024082)

В этом примере round0 - необязательный аргумент (я знаю, round0 можно определить как логический).

foo = function(a, round0) {
    a = a * pi
    if(!missing(round0)) round(a)
    else a
}

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

bar = function(b) {
    if(b > 10) round1=T
    foo(b, round1)
}

Если b ‹10, то round1 в bar () не определен, но все равно передается в foo. Если я изменю foo ():

foo = function(a, round0) {
    a = a * pi
    print(missing(round0))
    print(round0)
    if(!missing(round0)) round(a)
    else a
}

и запустите bar (9), результат будет:

bar(9)
[1] FALSE
Error in print(round0) : object 'round1' not found
Called from: print(round0)

Это означает: round0 не пропущен, но и недоступен?

Я не хочу использовать разные вызовы функций в bar (), если есть несколько необязательных аргументов в foo (), мне пришлось бы писать вызов функции для каждого отсутствующего / не отсутствующего - комбинации всех необязательных аргументов.

Можно ли пройти проверку «отсутствует» или какое другое решение применимо для этой проблемы?


person MarkusN    schedule 22.07.2015    source источник
comment
Обычно, если вы вызываете функцию в определенной форме, вы должны убедиться, что у вас есть все параметры. Здесь я бы добавил round1=F в начале bar и обновил foo на if(!missing(round0) && round0). Отсутствующие позволяют вам вызывать foo(a) или foo(a,T/F), если вы вызываете его с двумя параметрами, второй параметр не отсутствует и должен быть разрешаемым.   -  person Tensibai    schedule 22.07.2015
comment
Я предполагаю, что missing возвращает FALSE, как только объект обещания, представляющий аргумент функции, имеет непустой слот выражения. Добавьте строку print(substitute(round0)) сразу после a = a * pi в измененную функцию foo, а затем выполните foo(9). substitute извлечет слот выражения. Он ничего не напечатает, то есть пустой слот выражения для round0. Теперь попробуйте bar(9). Это напечатает round1. Но когда вы используете print, R попытается оценить round1, который еще не был определен (ленивая оценка).   -  person cryo111    schedule 22.07.2015


Ответы (4)


В вашем примере round0 не отсутствует, для него установлено значение round1, которое не определено (в отличие от отсутствия).

В общем, лучший способ сделать это - использовать значение по умолчанию, в вашем случае FALSE:

foo = function(a, round0 = FALSE) {
    a = a * pi
    if (!round0) round(a)
    else a
}

bar = function(b) {
    round1 <- FALSE
    if (b > 10) round1=TRUE
    foo(b, round1)
}

или где значение по умолчанию не может быть легко выражено в списке параметров:

foo = function(a, round0 = NULL) {
    a = a * pi
    if(!is.null(round0)) round(a)
    else a
}

bar = function(b) {
    round1 <- NULL
    if (b > 10) round1=TRUE
    foo(b, round1)
}

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

Вы также можете вызвать вашу foo функцию с аргументом или без него, если необходимо, в вашем if выражении:

bar = function(b) {
    if (b > 10) foo(b, TRUE) else foo(b)
}

Альтернативный подход, показывающий, как сгенерировать отсутствующее значение, показан в ответе @ moody_mudskipper.

person Nick Kennedy    schedule 22.07.2015
comment
Последнее утверждение неточно. Вы можете сделать переменную отсутствующей, определив ее как quote(expr=), substitute() или alist(x=1)[[1]]. Первый из них наиболее эффективен. {rlang} имеет missing_arg функцию, которая тоже делает то же самое. См. Мой собственный ответ ниже. - person Moody_Mudskipper; 03.12.2020
comment
@Moody_Mudskipper спасибо, я этого не знал. Я изменил свой ответ, чтобы указать на ваш. - person Nick Kennedy; 03.12.2020

Я недавно столкнулся с подобной проблемой и хотел решить ее в общих чертах. Я думаю, что это можно сделать, как показано в определении функции g() ниже:

f <- function(a = 5, b = 3, c = 2, d = 7) {
  if (missing(a)) {print("a is missing.")}
  if (missing(b)) {print("b is missing.")}
  if (missing(c)) {print("c is missing.")}
  if (missing(d)) {print("d is missing.")}

  cat(a, b, c, d)
}

g <- function(a = 1, b = 1, c = 1, d = 1) {
  args <- as.list(match.call())
  args[[1]] <- NULL # remove first list element, it's the function call
  do.call(f, args, envir = parent.frame())
}

Вот что мы получаем при вызове g() с разными наборами аргументов:

> g()
[1] "a is missing."
[1] "b is missing."
[1] "c is missing."
[1] "d is missing."
5 3 2 7

> g(a = 3)
[1] "b is missing."
[1] "c is missing."
[1] "d is missing."
3 3 2 7

> g(b = 10, c = 10)
[1] "a is missing."
[1] "d is missing."
5 10 10 7

Вы можете добавить или удалить из списка args, если вы не хотите передавать все аргументы следующей функции или хотите добавить некоторые. В качестве примера рассмотрим следующую функцию g(), которая делает это обычным образом:

g <- function(a = 1, b = 1, c = 1, x = 1, y = 1, z = 1) {
  f_args <- c("a", "b", "c") # arguments we want to hand off to function f

  # obtain the list of arguments provided
  args <- as.list(match.call())
  # remove first list element, it's the function call
  args[[1]] <- NULL
  # remove the arguments that are not listed in f_args
  args <- args[na.omit(match(f_args, names(args)))]

  # now add argument d, we always want it to be 0:
  args <- c(args, list(d = 0))
  do.call(f, args, envir = parent.frame())
}

Вот что мы получаем при вызове g() с разными наборами аргументов:

> g()
[1] "a is missing."
[1] "b is missing."
[1] "c is missing."
5 3 2 0

> g(a = 3)
[1] "b is missing."
[1] "c is missing."
3 3 2 0

> g(b = 10, c = 10)
[1] "a is missing."
5 10 10 0

См. этот ответ для получения дополнительной информации о do.call().

person Claus Wilke    schedule 18.09.2017

Вы можете создать недостающий объект, используя substitute() без аргументов.

В вашем случае мы могли бы сделать round1 отсутствующим объектом в предложении else:

foo = function(a, round0) {
  a = a * pi
  if(!missing(round0)) round(a)
  else a
}

bar = function(b) {
  if(b > 10) round1=T else round1 <- substitute()
  foo(b, round1)
}

bar(9)
#> [1] 28.27433

Создано 24.10.2019 с помощью пакета REPEX (v0.3.0)

person Moody_Mudskipper    schedule 24.10.2019
comment
missing() нужен ввод, чтобы быть чем-то (в этом отношении это название оксюморон). Это что-то является пустым выражением, и кажется, substitute() может создавать некоторые, вы также можете делать alist(x=)[[1]]. Для подробного ответа нам нужно перейти к реализации C, а я мало что знаю об этом. - person Moody_Mudskipper; 07.10.2020

rlang также предоставляет функцию missing_arg(), которая заставляет аргумент отсутствовать.

foo = function(a, round0) {
  a = a * pi
  if(!missing(round0)) round(a)
  else a
}

bar = function(b) {
  if(b > 10) round1 <- TRUE else round1 <- rlang::missing_arg()
  foo(b, round1)
}

foo(9)
#> [1] 28.27433
bar(9)
#> [1] 28.27433

Создано 02.12.2020 с помощью пакета REPEX (v0.3.0)

person Matifou    schedule 03.12.2020