获取函数内部函数调用的函数组件

Dav*_*vid 4 r metaprogramming

是否可以检索函数调用的函数组件?也就是说,是否可以as.list(match.call())在另一个函数调用上使用。

背景是,我想要一个接受函数调用并返回所述函数调用的组件的函数。

get_formals <- function(x) {
  # something here, which would behave as if x would be a function that returns
  # as.list(match.call())
}

get_formals(mean(1:10))
# expected to get:
# [[1]]
# mean
#
# $x
# 1:10
Run Code Online (Sandbox Code Playgroud)

预期的结果是有get_formals回报match.call()是附带的函数调用中调用。

mean2 <- function(...) {
  as.list(match.call())
}
mean2(x = 1:10)
# [[1]]
# mean2
# 
# $x
# 1:10
Run Code Online (Sandbox Code Playgroud)

另一个例子

这个问题背后的动机是检查memoised 函数是否已经包含缓存值。memoise具有该功能,has_cache()但需要以特定方式调用has_cache(foo)(vals),例如,

library(memoise)

foo <- function(x) mean(x)
foo_cached <- memoise(foo)

foo_cached(1:10) # not yet cached
foo_cached(1:10) # cached

has_cache(foo_cached)(1:10) # TRUE
has_cache(foo_cached)(1:3) # FALSE
Run Code Online (Sandbox Code Playgroud)

我的目标是记录一些函数调用是否被缓存。

cache_wrapper <- function(f_call) {
  is_cached <- has_cache()() # INSERT SOLUTION HERE
  # I need to deconstruct the function call to pass it to has_cache
  # basically
  # has_cache(substitute(expr)[[1L]])(substitute(expr)[[2L]]) 
  # but names etc do not get passed correctly

  if (is_cached) print("Using Cache") else print("New Evaluation of f_call")
  f_call
}

cache_wrapper(foo_cached(1:10))
#> [1] "Using Cache"     # From the log-functionality
#> 5.5                   # The result from the function-call
Run Code Online (Sandbox Code Playgroud)

Dar*_*sai 6

你可以match.call()用来做参数匹配。

get_formals <- function(expr) {
  call <- substitute(expr)
  call_matched <- match.call(eval(call[[1L]]), call)
  as.list(call_matched)
}

get_formals(mean(1:10))

# [[1]]
# mean
# 
# $x
# 1:10

library(ggplot2)
get_formals(ggplot(mtcars, aes(x = mpg, y = hp)))

# [[1]]
# ggplot
# 
# $data
# mtcars
# 
# $mapping
# aes(x = mpg, y = hp)

library(dplyr)
get_formals(iris %>% select(Species))

# [[1]]
# `%>%`
# 
# $lhs
# iris
# 
# $rhs
# select(Species)
Run Code Online (Sandbox Code Playgroud)

编辑: 感谢@KonradRudolph 的建议!

上面的函数找到了正确的函数。它将在 的父级范围内搜索get_formals(),而不是在调用者的范围内搜索。更安全的方法是:

get_formals <- function(expr) {
  call <- substitute(expr)
  call_matched <- match.call(eval.parent(bquote(match.fun(.(call[[1L]])))), call)
  as.list(call_matched)
}
Run Code Online (Sandbox Code Playgroud)

match.fun()是由相同名称的非功能对象遮挡的正确解析功能重要。例如,如果mean被向量覆盖

mean <- 1:5
Run Code Online (Sandbox Code Playgroud)

第一个例子get_formals()会报错,而更新后的版本运行良好。

  • 请注意,这会找到*正确的*函数:它将在“get_formals”的父级范围内搜索,而不是在调用者的范围内搜索。您可以通过将 `eval` 替换为 `eval.parent(bquote(match.fun(.(call[[1L]]))))` 来解决此问题(这相当多,但 `match.fun` 对于正确解析被同名非函数对象隐藏的函数)。 (4认同)