我正处于以下情况:
some_env
)的自定义环境(some_function()
)。请注意,我不拥有这个环境,它是从另一个包导入的library(rlang)
# Define a custom environment where I store a function.
# This is done in another package.
some_env <- new.env(parent = emptyenv())
class(some_env) <- "custom_class"
some_env$some_function <- function(x) {
print(x)
}
# Make a user-facing function that calls the function stored in the custom
# environment under the hood
user_facing_fn <- function(val = 1) {
some_env$some_function(val)
}
user_facing_fn()
#> [1] 1
我的目标是捕获对这个内部函数的调用(
some_function()
)。我可以使用 user_facing_fn()
和 rlang::expr()
在 !!
内部执行此操作。然而,在实际情况中,我有许多面向用户的函数,因此使用这种组合会使代码变得非常混乱。
因此,我考虑定义一个自定义类和
$
调用,以便我可以在将其分派到下一个方法之前访问该调用。
# Define a custom `$` to access the call before evaluating it
`$.custom_class` <- function(x, name) {
fc <- rlang::frame_call()
### For debugging
cat("----------------\n")
print(fc)
cat("----------------\n")
print(str(fc))
cat("----------------\n")
print(deparse(fc))
###
NextMethod("$") # <<<<<<<<<<<<< Not interested in this part
}
看来我可以用
frame_call()
捕获呼叫并且它被正确打印(参见下面的第一行打印):
user_facing_fn()
#> ----------------
#> some_env$some_function(val)
#> ----------------
#> language `$.custom_class`(some_env, "some_function")
#> - attr(*, "srcref")= 'srcref' int [1:8] 14 3 14 29 3 29 14 14
#> ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x000001d40b90c0c0>
#> NULL
#> ----------------
#> [1] "`$.custom_class`(some_env, \"some_function\")"
#> [1] 1
但是,在将表达式存储在
val
中后,我找不到访问fc
的值的方法。如您所见,它被解析为 `$.custom_class`(some_env, "some_function")
但我无法访问 val
。我尝试了很多rlang
功能,但找不到有效的东西。
澄清一下,我想要的是当我在
val
内部时能够访问 1
的值(在上面的示例中,它将是 $.custom_class
)。这可能吗?我可以使用基本 R 或 rlang
。
编辑以下 MrFlick 的评论:
我的目标是
user_facing_fn()
返回输出和包含对内部函数的调用的属性,如下所示:
...
# Make a user-facing function that calls the function stored in the custom
# environment under the hood
user_facing_fn <- function(val = 1) {
my_expr <- expr(some_env$some_function(!!val))
out <- eval_bare(my_expr)
attr(out, "original_query") <- my_expr
out
}
user_facing_fn(3)
#> [1] 3
#> attr(,"original_query")
#> some_env$some_function(3)
问题是我有许多面向用户的函数,在其中一些函数中我想捕获对存储在
some_env
中的函数的多次调用。因此,我认为我可以自动捕获 expr()
中的调用,而不是重复这个 !!
+ $
,并且我不需要修改面向用户的函数(或稍微修改)。
由于各种评论,我想我找到了一种方法。
我只是把我的设置放回这里:
library(rlang)
# Define a custom environment where I store a function.
# This is done in another package.
some_env <- new.env(parent = emptyenv())
class(some_env) <- "custom_class"
some_env$some_function <- function(x) {
x
}
# Make a user-facing function that calls the function stored in the custom
# environment under the hood
user_facing_fn <- function(val = 1) {
some_env$some_function(val)
}
user_facing_fn(1)
#> [1] 1
我可以修改
some_env
中的函数,以便我可以获得调用并将其存储在某处(例如在属性中),然后再对其进行评估。首先,我需要 eapply()
来遍历 some_env
中存储的所有函数(在本例中,只有 some_function()
):
modify_env <- function(env) {
# Here I overwrite all functions defined in `some_env`. In this example,
# there's only one.
eapply(env, function(fun) {
function(...) {
# Evaluate the args that are passed to the `some_function()`, which is
# only "val" in this example.
# First we capture unevaluated args, then we evaluate each of them in the
# caller env.
fc <- as.list(frame_call())
# The first element is the call, which is `some_env$some_function()` here.
# It is not an argument so I remove it.
fc1 <- fc[[1]]
fc[[1]] <- NULL
fc <- lapply(fc, eval_bare, env = caller_env())
# Evaluate the call. This produces the output (which is equal to the
# input in this example).
out <- call2(fun, !!!fc) |> eval_bare()
# Build the call that will be stored in the attributes of the output so
# that show_query() can access it.
args <- list2(...)
full_call <- call2(fc1, !!!fc)
full_call <- deparse(full_call)
# Store the call in the attribute of the output.
attr(out, "original_expr") <- full_call
out
}
})
}
现在我通过克隆
some_env
来定义自己的环境,然后修改它:
my_new_env <- modify_env(
env_clone(some_env)
)
最后,我定义了一个自定义
$
以在评估之前访问对 some_env$some_function()
的调用。在这里,我没有返回 some_env$some_function
(它只会返回输入而不存储调用),而是从自定义环境返回修改后的函数:
`$.custom_class` <- function(x, name) {
my_new_env[[name]]
}
我们现在将原始调用存储在输出的属性中:
user_facing_fn()
#> [1] 1
#> attr(,"original_expr")
#> [1] "some_env$some_function(1)"
user_facing_fn(3)
#> [1] 3
#> attr(,"original_expr")
#> [1] "some_env$some_function(3)"