在给定承诺名称或形式参数的情况下评估 R 中的值

Evaluate a value in R given either the name of a promise or the formal argument

代码:

args <- function() {
  list(
    x = "How old",
    y = "is",
    z = "the car?")
}

fun1 <- function(x = "How old", y = "is", z = "the car") {
  # collect arguments
  args_used   <- as.list(match.call()[-1])

  # Call fun2() using the args supplied by the user
  do.call(fun2, args_used)

}

fun2 <- function(x = args()$x, y = args()$y, z = args()$z) {
  paste(x, y, z)
}

问题

使用 fun1() and/or fun2() 如果直接使用就可以,例如

> fun1("Where", "are", z = "the cars")
[1] "Where are the cars"

但是,如果我这样做:

list1 <- list(l1 = "Where", l2 = "Is this")
list2 <- list(l1 = "is", l2 = "the")
list3 <- list(l1 = "Peter", l2 = "new iPhone?")

mapply(function(i, j, k) fun1(x = i, y = j, z = k),
       i = list1,
       j = list2,
       k = list3,
       SIMPLIFY = FALSE)

我明白了

Error in paste(x, y, z) : object 'i' not found

我知道为什么会出现问题,我什至知道如何使用 eval(parse(text = "")) 修复它(尽管我知道 eval-parse 策略的潜在问题(参见,例如:this discussion)

因此重写 fun1() 如下:

fun1 <- function(x = "How old", y = "is", z = "the car") {
  # collect arguments
  args_used <- as.list(match.call()[-1])
  args_used <- lapply(names(args_used), function(w) eval(parse(text = w))) 

  # Call fun2() using the args supplied by the user
  do.call(fun2, args_used)

}

并随后使用 mapply(....) 如上所述工作。

然而这里就变得棘手了。

  1. 注意如果我将 lapply(names(args_used), function(w) eval(parse(text = w))) 中的 w 更改为 x 会发生什么(yz 也是如此。现在调用 mapply 同上,给出:
    $`l1`
    [1] "x is Peter"
    
    $l2
    [1] "x the new iPhone?"
    

    Clearly not what I wanted. Again I understand why this happens and a fix would be to not use x but something like w but any name a pick is from now on reserved. Not ideal.

  2. 如果通过 ... 参数调用其中一个参数,该方法也会中断。像这样修改 fun1()

    fun1 <- function(x = "How old", y = "is", ...) {
      # collect arguments
      args_used <- as.list(match.call()[-1])
      args_used <- lapply(names(args_used), function(x) eval(parse(text = x))) 
    
      # Call fun2() using the args supplied by the user
      do.call(fun2, args_used)
    
    }
    

    现在结果是

    Error in eval(parse(text = x)) : object 'z' not found

再次,我明白为什么(因为名称 "z"fun1() 环境中是未知的)但现在的问题是:

我该如何解决这个问题才能避免问题 1 和问题 2?

只需让您的函数计算调用环境中的参数即可。这似乎有效(注意添加 envir=parent.frame())。

fun1 <- function(x = "How old", y = "is", z = "the car") {
  args_used  <- as.list(match.call()[-1])
  do.call(fun2, args_used, envir=parent.frame()) 
}

但我根本不明白你为什么要在这里使用惰性求值。您可以将所有参数捕获为环境的一部分并传递它们的值

fun1 <- function(x = "How old", y = "is", z = "the car") {
  args_used  <- as.list(environment())
  # if you need ...: args_used <- c(as.list(environment()), list(...))
  do.call(fun2, args_used) 
}

请注意,environment() 将捕获您调用它时存在的所有变量。因此,如果您只需要传递给函数的变量,请务必在函数开始时调用它。

如果您的参数名称中有前导点,R 通常将它们视为 "hidden",因此您需要将 all.names=TRUE 参数设置为 as.list.environment 函数以获取它们.你可以做到

args_used  <- as.list(environment(), all.names=TRUE)