如何在函数调用中“list2env()”(或通过管道传递到“assign()”)?

How to `list2env()` (or pipe into `assign()`) within a function call?

我有一个用例,用于将函数映射到向量,然后将结果分配给父环境中的各个对象——不是全局环境,而是调用 map() 的环境。具体来说,这一切都发生在一个函数中,所以我想将这些对象分配给函数的环境,以便随后在函数调用中使用,而且只能在那里使用。

我知道您可以通过升序数字位置(全局为 1)或从当前环境倒数来指定环境,即使用 rlang::caller_env()。但是,通过任何一种方法,在这种情况下,我都没有可靠的方法来指定所需的函数执行环境。正如下面的 reprex 所示,我可以在使用 rlang::caller_env(6) 的特定情况下让它工作,但很明显,在这种情况下,恰好向后计数 6 帧恰好可以工作,它具有特定的管道链和操作顺序,并且任何情况都可能有任何其他需要的值——我只是通过在函数中打印回溯发现 6 是这里的正确数字。使用 map() 时,它是 13 左右,可能是因为 map() 中的所有嵌套函数调用。而且我根本无法使用 list2env()

所以我正在寻找的是我可以提供给 list2env()assign() 的一些论据,这些论据将清楚而一致地导致赋值发生在我所定义的函数的环境中在内部使用它们,即使我在管道链的末尾调用这些函数。


library(tidyverse)
library(rlang, warn.conflicts = FALSE)

## Trying to assign the value 'foo' to an object named 'bar' in a specific
## location

# Fails because `bar` is assigned in the pipe execution evironment
'foo' %>% assign(x = 'bar')
exists('bar')
#> [1] FALSE

# Works because `pos = 1` refers specifically to the global environment
'foo' %>% assign(x = 'bar', pos = 1)
exists('bar')
#> [1] TRUE
rm(bar)

# Works because assign isn't in a pipe, and its special, default `pos` of
# `-1` refers to the function execution environment, as desired
exec(function() {
  assign('bar', 'foo')
  exists('bar', inherits = FALSE)
})
#> [1] TRUE
rm(bar)
#> Warning in rm(bar): object 'bar' not found

# Fails because the function's exec. env. is "overshot," and the assignment
# occurs in the global environment instead; no numeric position seems to work
exec(function() {
  'foo' %>% assign(x = 'bar', pos = 1)
  exists('bar', inherits = FALSE)
})
#> [1] FALSE
rm(bar)

# Works, presumably because the function's exec. env. happens to be exactly 6
# frames back from the environment in which the `assign()` call is evaluated, in
# this specific case
exec(function() {
  'foo' %>% assign(x = 'bar', pos = caller_env(6))
  print(exists('bar', inherits = FALSE))
  print(bar)
})
#> [1] TRUE
#> [1] "foo"

# Fails for unknown reasons - maybe there's a `caller_env()` value that would
# work, but I haven't found it
exec(function() {
  list <- list(bar = 'foo')
  list2env(list, envir = caller_env())
  exists('bar', inherits = FALSE)
})
#> [1] FALSE

reprex package (v0.3.0)

于 2020-10-27 创建

最可靠最简单的方法是将函数的环境存储在名称中并引用该名称。

f = function () {
    env = environment()
    'foo' %>% assign('bar', envir = env)
    foo
}

如果您不使用管道,list2env 可以直接使用:

g = function () {
    list = list(foo = 'bar')
    list2env(list, envir = environment())
    foo
}

但在这两种情况下,我 通常 建议坚持使用列表并避免将变量分配给调用环境;以下等同于 g:

g2 = function () {
    list = list(foo = 'bar')
    list$foo
}