如何访问存储在自定义环境中的函数调用的参数?

问题描述 投票:0回答:1

我正处于以下情况:

  • 我使用存储函数列表(例如
    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()
中的调用,而不是重复这个
!!
+
$
,并且我不需要修改面向用户的函数(或稍微修改)。

r rlang
1个回答
0
投票

由于各种评论,我想我找到了一种方法。

设置

我只是把我的设置放回这里:

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)"
© www.soinside.com 2019 - 2024. All rights reserved.