R 将命名参数传递给调用未变异函数的函数
我正在尝试做一些非常类似的事情 本质上,我需要将一个命名列表传递给一个函数,并将该命名列表作为其参数传递给另一个函数 如果我们遵循这个链接,他们就可以用mutate来实现,而我可以复制它:R 将命名参数传递给调用未变异函数的函数,r,tidyverse,rlang,tidy,tidyeval,R,Tidyverse,Rlang,Tidy,Tidyeval,我正在尝试做一些非常类似的事情 本质上,我需要将一个命名列表传递给一个函数,并将该命名列表作为其参数传递给另一个函数 如果我们遵循这个链接,他们就可以用mutate来实现,而我可以复制它: df <- tribble( ~a, 1 ) foo <- function(x, args) { mutate(x, !!! args) } foo(df, quos(b = 2, c = 3) # A tibble: 1 x 3 a b
df <- tribble(
~a,
1
)
foo <- function(x, args) {
mutate(x, !!! args)
}
foo(df, quos(b = 2, c = 3)
# A tibble: 1 x 3
a b c
<dbl> <dbl> <dbl>
1 1 2 3
df您可以在函数中使用match.call()
来获取参数列表及其名称:
myfun列表(a=1,b=“hi”,c=“a”)
#>
#>b美元
#> [1] 5
#>$x
#>[1]“呼叫”
#>
#>b美元
#>[1]“数字”
您需要blast()
,它可能包含在rlang 0.5.0中
blast <- function(expr, env = parent.frame()) {
rlang::eval_bare(rlang::enexpr(expr), env)
}
blast(cbind(!!!letters))
blast我认为区分文字值(又称常量)和未赋值表达式很重要。例如,quos(b=2,c=3)
将始终计算为2和3,而与上下文无关。在这种情况下,您不需要将它们设置为QUOSURE或表达式,一个简单的值列表就可以了。然后,您可以使用purrr::lift
将任意函数从获取..
点转换为获取列表。没有代码>需要:
arglist <- list( replace=TRUE, size=5, x=1:10 ) # Note: list, not quos
sample2 <- purrr::lift(sample)
sample2( arglist ) # Same as sample( x=1:10, size=5, replace=TRUE)
# [1] 7 3 10 8 3
在上面的例子中,subset2()
手工构造一个子集(x、arg1、arg2等)
表达式,然后对其求值。curly-curly操作符用作的快捷方式!!enquo(x)
将用户表达式直接粘贴到最终表达式中,而rlang::list2()
展开并拼接所有其他参数。通过使用rlang::list2()
而不是base:list()
我们正在添加对的支持代码>将函数作为一个整体
还值得强调的是rlang::exec()
和rlang::call2()
,它们与基本的do.call和call相当。两者都提供无缝的参数拼接支持,支持代码>:
subset2 <- function( x, ... )
rlang::eval_tidy(rlang::expr(subset( {{x}}, !!!rlang::list2(...) )))
# Capture expressions because mpg and cyl are undefined at this point
argexpr <- rlang::exprs( mpg < 15, select=cyl )
# base::subset() doesn't support !!!, but our new function does!
subset( mtcars, !!!argexpr ) # Error in !argexpr : invalid argument type
subset2( mtcars, !!!argexpr ) # Same as subset( mtcars, mpg < 15, select=cyl )
mtcars %>% subset2(!!!argexpr) # Also works with the pipe
# cyl
# Duster 360 8
# Cadillac Fleetwood 8
# ...
rlang::exec( sample, !!!arglist )
eval(rlang::call2( subset, mtcars, !!!argexpr ))
最后,@Moody_mudscapper有一个非常好的例子。其中一个标签为任何任意函数添加NSE支持,并与%%>%%
完全集成:
library(tags) ## installed with devtools::install_github("moodymudskipper/tags")
using_bang$sample( !!!arglist )
using_bang$subset( mtcars, !!!argexpr )
mtcars %>% using_bang$subset( !!!argexpr )
tidyverse在tidyverse函数中工作。是否有办法在给blast的列表中包含一个未命名的参数,或者单独包含另一个参数?也就是说,如果我的函数有3个参数,我想在第一个参数中使用管道,而在其他2个参数中使用blast?代码类似于df_to_apply_to%>%blast(func_to_apply(!!!其他参数))
blast()
不是要通过管道传输到。它必须包装应该发生准旋转的表达式。所以:blast(df%>%f(!!!other))
subset2()
函数非常奇怪。我不明白它想做什么。你好,莱昂内尔汉里。这基本上与相同,只是我在这里使用list2()
代替enquos()
,因为表达式已经在argexpr
中捕获,所以不需要另一个级别的引用。我将补充一些说明。作为旁注,您的技巧对于“修复”使用NSE处理map()
和%>%%
的基函数非常有用。这包括,list2()
启用了代码>内部…
和外部的blast()
将参数转发到subset()
。我想知道我们是否能使这种模式更容易。顺便说一下,我认为最好在subset2()
的签名中复制尽可能多的命名参数。@LionelHenry:是的,你说得对。我尽量保持简单,因为我的答案已经有点长了。但是我认为在这种情况下保留x
很重要,特别是因为OP想要使用%>%
。
library(tags) ## installed with devtools::install_github("moodymudskipper/tags")
using_bang$sample( !!!arglist )
using_bang$subset( mtcars, !!!argexpr )
mtcars %>% using_bang$subset( !!!argexpr )