首页 > 解决方案 > 如何获取在指定环境中评估调用对象时使用的所有参数的值

问题描述

说我有一个f功能

f = function(x = 1, y, z, t) { x + y + z}

l和这样的清单

l = list(Y = 2, t = "test")

我可以fl这样评价

eval(quote(f(y = Y, z = 3)), envir = l)
6

我的问题是,我想获取最终被函数使用的参数的所有值,f即。一个函数magic,它将接受一个调用对象和一个环境,并返回将用于评估表达式的所有参数的值。

例如:

call_obj = quote(f(y = Y, z = 3))
magic(call_obj, envir = l)
# I get a named list which value is list(1,2,3,"test")
# For that matter I do not even need the default arguments values (x)

编辑:为 base-r 答案添加赏金(虽然@Artem Sokolov 提供了一个 purrr-rlang 答案,但提取几个相关函数仍然可以)

标签: rnsepryr

解决方案


这个怎么样:

magic <- function(call_obj, envir) {
  
  call_fun <- as.list(as.call(call_obj))[[1]]
  
  call_obj <- match.call(match.fun(call_fun), as.call(call_obj))

  
  ## arguments supplied in call
  call_args <- as.list(call_obj)[-1]
  
  
  ## arguments from function definition
  fun_args <- formals(match.fun(call_fun))
  
  ## match arguments from call with list
  new_vals_call <- lapply(call_args, function(x) eval(x, envir = envir))
  
  ## match arguments from function definition with list
  ## because everything (including NULL) can be a valid function argument we cannot directly use mget()
  
  in_list <- sapply(names(fun_args), function(x, env) exists(x, envir = env), as.environment(envir))
  new_vals_formals <- mget(names(fun_args), envir = as.environment(envir), ifnotfound = "")[in_list]
  
  
  ## values in the call take precedence over values from the list (can easily be reversed if needed)
  new_vals_complete <- modifyList(fun_args, new_vals_formals, keep.null = TRUE)
  new_vals_complete <- modifyList(new_vals_complete, new_vals_call, keep.null = TRUE)
  
  ## Construct a call object (if you want only the list of arguments return new_vals_complete)
  as.call(c(call_fun, new_vals_complete))
}


# -------------------------------------------------------------------------


f <- function(x = 1, y, z, t) { x + y + z}


## Tests

## basic test
magic(quote(f(y = Y, z = 3)), list(Y = 2, t = "test"))
#> f(x = 1, y = 2, z = 3, t = "test")    

## precedence (t defined twice)
magic(quote(f(y = Y, z = 3, t=99)), list(Y = 2, t = "test"))
#> f(x = 1, y = 2, z = 3, t = 99)

## missing values (z is missing)
magic(quote(f(y = Y)), list(Y = 2, t = "test"))
#> f(x = 1, y = 2, z = , t = "test")

## NULL values in call
magic(quote(f(y = Y, z = NULL)), list(Y = 2, t = "test"))
#> f(x = 1, y = 2, z = NULL, t = "test")

## NULL values in list
magic(quote(f(y = Y, z = 3)), list(Y = 2, t = NULL))
#> f(x = 1, y = 2, z = 3, t = NULL)

## NULL values and precendece
magic(quote(f(y = Y, z = 3, t= NULL)), list(Y = 2, t = "test"))
#> f(x = 1, y = 2, z = 3, t = NULL)
magic(quote(f(y = Y, z = 3, t=99)), list(Y = 2, t = NULL))
#> f(x = 1, y = 2, z = 3, t = 99)

## call with subcalls
magic(quote(f(y = sin(pi), z = 3)), list(Y = 2, t = "test"))
#> f(x = 1, y = 1.22460635382238e-16, z = 3, t = "test")
magic(quote(f(y = Y, z = 3)), list(Y = sin(pi), t = "test"))
#> f(x = 1, y = 1.22460635382238e-16, z = 3, t = "test")

## call with additional vars (g is not an argument of f)  -> error: unused arguments
magic(quote(f(g = Y, z = 3)), list(Y = 2, t = "test"))

## list with with additional vars (g is not an argument of f) -> vars are ignored 
magic(quote(f(y = Y, z = 3)), list(Y = 2, t = "test", g=99))
#> f(x = 1, y = 2, z = 3, t = "test")

## unnamed arguments
magic(quote(f(99, y = Y, z = 3)), list(Y = 2, t = "test"))
#> f(x = 99, y = 2, z = 3, t = "test")
magic(quote(f(99, y = Y, 77)), list(Y = 2, t = "test"))
#> f(x = 99, y = 2, z = 77, t = "test")

推荐阅读