【发布时间】:2019-12-06 04:30:10
【问题描述】:
我正在寻找一个函数,它将返回填充了参数的函数体。目标是有一个函数,capture_code,这样
my_scatterplot <- function(my_title, xlab = "mpg", ylab = "hp") {
g <- ggplot(mtcars, aes(x = mpg, y = hp)) +
geom_point()
g + labs(x = xlab, y = ylab, title = my_title)
}
capture_code(my_scatterplot("My title", xlab = "MPG"))
会回来
g <- ggplot(mtcars, aes(x = mpg, y = hp)) +
geom_point()
g + labs(x = "MPG", y = ylab, title = "My title")
我正在使用来自advanced R Walking AST with recursive functions 的代码。
expr_type <- function(x) {
if (rlang::is_syntactic_literal(x)) {
"constant"
} else if (is.symbol(x)) {
"symbol"
} else if (is.call(x)) {
"call"
} else if (is.pairlist(x)) {
"pairlist"
} else {
typeof(x)
}
}
switch_expr <- function(x, ...) {
switch(expr_type(x),
...,
stop("Don't know how to handle type ", typeof(x), call. = FALSE)
)
}
replace_vars <- function(x, envi) {
switch_expr(x,
# Base cases
constant = x,
symbol = {
# Get the variable from the environment
rlang::env_get(nm = as.character(x), default = x, env = envi)
},
# Recursive cases
pairlist = purrr::map(x, replace_vars, envi),
call = {
res <- purrr::map(x, replace_vars, envi)
class(res) <- class(x)
res
}
)
}
capture_code <- function(e) {
e <- rlang::enexpr(e)
cf <- get(toString(e[[1]]))
if(typeof(cf) != "closure") stop(e[[1]], "is not a function")
# Evalation the named functions first
# Then fill in the unnamed
cf_args <- formals(cf)
called_args <- as.list(e[-1])
if(!is.null(names(called_args))) {
not_named <- names(called_args) == ""
named_args <- called_args[!not_named]
unnamed_args <- called_args[not_named]
new_args <- modifyList(cf_args, named_args)
missing_args <- unlist(lapply(new_args, rlang::is_missing))
missing_indices <- seq_along(new_args)[missing_args]
} else {
new_args <- cf_args
unnamed_args <- called_args
missing_indices <- seq_along(new_args)
}
# Add the unnamed arguments
for(i in seq_along(unnamed_args)) {
new_args[[missing_indices[[i]]]] <- unnamed_args[[i]]
}
# Get the function body from
cf_func_body <- functionBody(cf)[-1]
# Pass the arguments as an environment for lookup
replace_vars(cf_func_body, rlang::new_environment( as.list(new_args)))
}
res <- capture_code(my_scatterplot("My title", xlab = "MPG"))
res
我已经包含了来自函数体表达式的 View 调用以及我的结果。它看起来几乎是正确的,除了我无法将call 和<- 类设为language 类型。我希望能够从我的 AST 中取回代码。
【问题讨论】:
-
这个有用例还是只是一个难题?因为如果给定的参数是变量或表达式而不是字符串文字,那么您在这里尝试做的事情不会有效,也不一定正确。考虑一下这样一种情况:您有一个参数
i = i +1会被多次替换,或者只是i=j在调用i的定义之前在函数体中修改了j。 -
用例是能够返回由在闪亮应用程序中的函数中创建的绘图产生的代码。像shinymeta 之类的东西,除了我有一个函数可以创建多个图。我将这个问题保持原样,但由于变量没有显示它们的值,它实际上并没有解决我描述的用例。
-
也许包 oshka 会有所帮助
标签: r metaprogramming rlang