将外部函数的默认值传递给R

时间:2017-08-25 20:44:29

标签: r

  

这个问题与我原来的不一样;它更多地依赖于 minimal 可重现的示例,并包含be_green的建议,以防止在函数的上下文中静默加载整个库。

外部函数首先定义多个案例,默认值和任何案例例外列表。除非定义了异常,否则内部函数通过使用计算中的默认值来组装每种情况。最后,外部函数将这些情况组合成一个数据框。

这是功能:

outerfun <- function(cases, var_default, exceptions=list()){
  # Inner Function to create a case
  innerfun <- function(var=var_default) {  # Case
    result = var
    return(result)
  }
  # Combine Cases
  datlist <- list()
  for(case in 1:cases){
    datlist[[paste0("X",case)]] <- do.call(innerfun, as.list(exceptions[[paste0("X",case)]]))
  }
  casedata <- do.call(dplyr::data_frame, datlist)
  return(casedata)
}

当我将内部函数的值定义为例外时,此函数正常工作:

data <- outerfun(cases = 3, var_default = 10, exceptions = list("X2" = c(var = 14)))

但是当我把两者混合时不是这样的:

data <- outerfun(cases = 3, var_default = 10, exceptions = 
                  list("X2"  = c(var = var_default + 4)))

能够将两者混合是很重要的,因为它使功能更直观,更容易为各种情况编程。

我认为问题可能来自使用do.call并且已经看到其他线程详细说明了这个问题(与环境和框架有关),但我还没有能够为我找到最佳解决方案。我喜欢do.call,因为我可以将一个参数列表传递给一个函数。我可以将内部函数转换为一个列表(想想:function(...){})但是我必须定义每个变量而不是依赖于默认值。

您可能获得的任何帮助或建议都会很棒。

2 个答案:

答案 0 :(得分:1)

问题是lvl_default未在函数的上下文之外定义,但您将其称为参数的输入。因为在全局环境中没有名为lvl_default的变量,所以当函数尝试计算参数exceptions = list(X3 - c(lvl = lvl_default + 10)时,它无法找到要评估的变量。您无法通过将参数设置为等于其他未评估参数的名称来指定参数。

相反,我建议做的是在与您希望传递给lvl_default的值相关联的函数之外设置一个变量,然后将其传递给函数,如下所示:

level <- 1000

data <- genCaseData(n_signals = 3, datestart = "2017-07-01T15:00:00", 
        n_cycles = 4, period_default = 10, phase_default = 0, ampl_default = 15, 
        lvl_default = level, exceptions = list(X1= c(lvl=980), 
        X3 = c(lvl = level + 10)))

正如我在评论中指出的那样,我建议不要在函数的上下文中静默加载整个库。你可以最终掩盖你不想要的东西,并遇到奇怪的错误,因为如果库不可用,require调用实际上不会抛出一个。相反,我会通过pkgname::fncname引用这些功能。

答案 1 :(得分:0)

be_green 首先解决了这个问题,但我想跟进我实际为项目做的事情。

正如 be_green 指出的那样,我无法在例外列表中调用var_default,因为它尚未定义。我一开始并不理解这一点,因为你可以实际定义函数本身定义的变量的参数的默认值:

addfun <- function(x, y = z + x + 2) { 
           z = 20
           c(x, y)
          }
addfun(x = 20)

[1] 20 42

这是因为R lazily evaluated中的函数参数。我认为这给了我一个传递来调用这个函数:

addfun(x = 10, y = x + z)

addfun中的错误(x = 10,y = x + z):object&#39; x&#39;找不到

如果您移除x,则会调用z的错误。因此,即使y的默认值取决于xz,您也无法使用xz来调用该函数。

be_green 建议我在字符串中传递参数,然后在函数中解析它。但我担心我的团队中的其他人会发现结果语法令人困惑。

相反,我使用了省略号(...)并评估了函数中的省略号参数。我使用这行代码完成了这个:

list2env(eval(substitute(alist(...))), envir = as.environment(-1))

此处eval(substitute(alist(...)))模式很常见,但会生成一个命名的参数列表。由于某些其他功能,将参数作为函数内的对象进行评估变得更加方便。 list2env(x, envir = as.environment(-1))通过额外的步骤完成此任务。调用参数后,您需要显式评估调用。因此,如果我想更改上面的addfun()

addfun <- function(x, ...) { 
           z = 20
           list2env(eval(substitute(alist(...))), 
                      envir = as.environment(-1))
           c(x, eval(y))
          }
addfun(x = 10, y = x + z)

这是一个陈腐的例子:我现在需要定义y,即使它不是函数中的参数。但现在我甚至可以在函数调用中重新定义z

addfun(x = 10, y = z + 2, z = 10)

由于non-standard evaluation,这一切都是可能的。可以进行权衡,但在我的非标准评估应用中,我能够提高功能的可用性和灵活性,同时使其更直观易用。

最终代码:

outerfun <- function(caseIDs, var_default, ...){
  list2env(eval(substitute(alist(...))), envir = as.environment(-1))
  # Inner Function to create a case
  innerfun <- function(var=var_default) {  # Case
    result = var
    return(result)
  }
  # Combine Cases
  datlist <- lapply(caseIDs, function(case) {
    do.call(innerfun, eval(get0(case, ifnotfound = list())))
  })
  names(datlist) <- caseIDs
  casedata <- do.call(dplyr::data_frame, datlist)
  return(casedata)
}

现在这两个示例都具有完整功能:

data <- outerfun(caseIDs = c("X1","X2","X3"), var_default = 10, 
         X2 = list(var = 14))
data <- outerfun(caseIDs = c("X1","X2","X3"), var_default = 10, 
         X2 = list(var = var_default + 4))

我希望这有助于其他人!享受!