【发布时间】:2021-12-22 23:55:32
【问题描述】:
我想将在函数外部定义的对象的内容传递给函数体。 举个例子,我的问题可能更容易理解:
a <- 'hello world'
foo <- function() print(a)
rm(a)
foo()
目前,这显然会引发错误,因为在调用foo() 时不再定义a。
我想要的结果是我希望 foo 看起来像 function() print('hello world')
【问题讨论】:
我想将在函数外部定义的对象的内容传递给函数体。 举个例子,我的问题可能更容易理解:
a <- 'hello world'
foo <- function() print(a)
rm(a)
foo()
目前,这显然会引发错误,因为在调用foo() 时不再定义a。
我想要的结果是我希望 foo 看起来像 function() print('hello world')
【问题讨论】:
1) 本地 像这样使用local 捕获a。这会将a 的值固定为定义foo 时的值。请注意,a <- a 右侧的 a 是已定义的 a,而左侧的 a 是一个新对象,也称为 a,它在 @987654330 中定义@。没有使用任何包。
a <- 'hello world'
foo <- local({
a <- a
function() print(a)
})
rm(a)
foo()
## [1] "hello world"
2) 替代 另一种替代方法是像这样将a 的值替代到foo 的主体中。这会将foo 中的a 修复为运行substitute 时的值。没有使用任何包。
foo <- function() print(a)
a <- 'hello world'
body(foo) <- do.call("substitute", list(body(foo), list(a = a)))
rm(a)
foo()
## [1] "hello world"
3) 生成 foo 的函数 另一种可能性是创建一个生成 foo 的函数。注意R使用惰性求值,所以我们需要使用force来保证a在foogen运行时的值被使用;否则,它将延迟评估,直到 a 被删除,导致错误。这会将foo 中的a 修复为传递给foogen 的值。没有使用任何包。
foogen <- function(a) {
force(a)
function() print(a)
}
a <- 'hello world'
foo <- foogen(a)
rm(a)
foo()
## [1] "hello world"
4) proto proto 包可用于创建对象p,其中包含foo 和a。
library(proto)
p <- proto(foo = function(.) print(a))
a <- 'hello world'
p$a <- a
rm(a)
p$foo()
## [1] "hello world"
【讨论】:
这里不需要使用eval(parse(...)),也不需要使用外部包。使用eval 和parse 是generally not a good idea for a few different reasons,包括恶意用户利用它的乐趣。随着 R 在接受用户输入的网络应用中的使用越来越多,这一点变得越来越重要。
在没有eval(parse(...)) 或任何外部依赖项的情况下,有几种方法可以在基本 R 中执行此操作。一种方法是拥有一个函数工厂:
a <- 'hello world'
foo_maker <- function(x) as.function(list(x = a, quote(print(x))))
所以现在我们可以这样做了:
foo <- foo_maker(a)
rm(a)
foo()
#> [1] "hello world"
【讨论】:
刚刚发现如何使用whisker 包和eval(parse(...)) 解决问题:
library(whisker)
a <- 'hello world'
foo <- eval(parse(text = whisker.render('function() print("{{b}}")',
data = list(b = a))))
foo()
rm(a)
foo()
【讨论】: