【问题标题】:Passing missing argument from function to function in R在R中将缺少的参数从函数传递到函数
【发布时间】:2015-10-12 00:20:13
【问题描述】:

我了解到在函数中使用可选参数并使用 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

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() 中有多个可选参数,我将不得不为每个丢失/未丢失的所有可选参数的组合编写一个函数调用。

是否可以通过“缺失”,或者其他解决方案适用于这个问题?

【问题讨论】:

  • 通常,如果你以特定的形式调用一个函数,你应该确保你有所有的参数。在这里,我将在bar 的开头添加一个round1=F,并将foo 更新为if(!missing(round0) && round0)。缺少允许你调用foo(a)foo(a,T/F),如果你用两个参数调用它,第二个参数不会丢失并且必须是可解析的。
  • 我的猜测是missing 在表示函数参数的承诺对象具有非空表达式槽时立即返回FALSE。在修改后的foo 函数中在a = a * pi 之后添加行print(substitute(round0)),然后执行foo(9)substitute 将提取表达式槽。它不会打印任何内容,即round0 的空表达式槽。现在试试bar(9)。这将打印round1。但是当您使用print 时,R 将尝试评估尚未定义的round1(惰性评估)。

标签: r arguments function-calls


【解决方案1】:

在您的示例中,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)
}

请注意,在这两种情况下,您都需要在调用函数中手动将参数设置为默认值。

如果需要,您还可以在 if 语句中调用带有或不带参数的 foo 函数:

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

@moody_mudskipper 的answer 展示了另一种显示如何生成缺失值的方法。

【讨论】:

  • 最后的说法不准确。您可以通过将变量定义为quote(expr=)substitute()alist(x=1)[[1]] 来使变量丢失。前者效率最高。 {rlang} 有一个 missing_arg 函数,它也有同样的作用。请参阅下面我自己的答案。
  • @Moody_Mudskipper 谢谢,我不知道。我已经修改了我的答案以指向你的答案。
【解决方案2】:

我最近遇到了类似的问题,想用一般的方式解决。我觉得可以按照下面函数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() 的更多信息,请参阅this answer

【讨论】:

    【解决方案3】:

    您可以使用 substitute() 不带参数来创建缺失对象。

    在您的情况下,我们可以在 else 子句中将 round1 设为缺失对象:

    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
    

    reprex package (v0.3.0) 于 2019 年 10 月 24 日创建

    【讨论】:

    • missing() 需要它的输入是某种东西(它的名字在这方面是矛盾的)。这个东西是一个空表达式,看起来substitute()可以创建一些,你也可以做alist(x=)[[1]]。对于冗长的答案,我们需要进入 C 实现,我对此了解不多。
    【解决方案4】:

    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
    

    reprex package 创建于 2020-12-02 (v0.3.0)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 2023-01-22
      • 1970-01-01
      • 2012-06-07
      相关资源
      最近更新 更多