基础R:替换函数调用的参数名称

6
问题的最终目标是使用在语言计算的基础上构建以下未评估调用,其中lista_name50L由参数提供。
list(a_name = 50L)

内部看起来像

str(quote(list(a_name = 50L)))
# language list(a_name = 50L)

str(as.list(quote(list(a_name = 50L))))
#List of 2
# $       : symbol list
# $ a_name: int 50

我会把我的变量放入一个列表中,这样代码会更加简洁。
params = list(my_fun = as.name("list"), my_name = "a_name", my_value = 50L)

# What I tried so far?

# 1. The first thing that one would try

substitute(my_fun(my_name = my_value),
           params)
#list(my_name = 50L) ## `my_name` was not substituted!

# 2. Workaround get the same output, but only after `setNames` call evaluation, so doesn't really answer the question about constructing specific call

substitute(setNames(my_fun(my_value), my_name), ## alternatively could be `structure(..., names=my_name)`
           params)
#setNames(list(50L), "a_name")

# 3. Another workaround, not really computing on the language but parsing, and integer L suffix is gone!

with(expr = parse(text=paste0(my_fun, "(", my_name, " = ", my_value, ")"))[[1L]],
     data = params)
#list(a_name = 50)

# 4. Working example using rlang

with(expr = rlang::call2(my_fun, !!my_name := my_value),
     data = params)
#list(a_name = 50L)

在基础的中有没有构建必需调用的方法? 基本上,要获得与rlang方式完全相同的输出,但使用基础

请注意,这个问题不是this的重复,该问题严格要求使用rlang解决方案。这个问题要求以基础的方式实现它。如果没有办法实现,我也想知道。谢谢。


str2lang(paste("", "list", " (", "a_name", " = ", "50L", ")")) 能够得到你想要的结果吗? - jay.sf
@jay.sf,“str2lang”只是“parse”的特殊版本,因此并不比示例3更好。 - jangorecki
1个回答

7
假设问题是如何生成一个call对象,这个对象的函数是params的第一个参数,其单个参数名称是params的第二个组件的值,其参数值是params的第三个组件的值,那么c2就是这个call对象。我们验证生成的call对象与问题中显示的rlang call对象完全相同。
c2 <- as.call(setNames(params, c("", "", params2$my_name))[-2])

# verify that c2 equals the output of the rlang example in question
c1 <- with(expr = rlang::call2(my_fun, !!my_name := my_value), data = params)

identical(c1, c2)
## [1] TRUE

这可以推广到调用有更多参数的情况,使params具有长度为n+2的第二个组件为名称向量(其长度为n),它仍然有效。
如果您控制输入,则我认为将输入列表定义为具有一个组件用于函数名称和每个参数的一个组件,并且组件的名称为参数名称(而不是单独的参数来保存名称)会更有意义。在这种情况下,我们可以简单地编写以下内容。实际上,以上内容是将params转换为该形式,然后使用as.call,因此如果可能的话,我们最好从一开始就提供该形式。
as.call(params2)  # params2[[1]] is func name, params2[-1] is named args

注意

执行调用只需使用:

eval(c2)

或者我们可以使用do.call

do.call(as.character(params[[1]]), setNames(tail(params, -2), params[[2]]))

1
感谢您的详细解释。同意,通过替换元素名称确实可以实现。我希望在基本的R API中有更简洁的方法,但我认为并没有。对于复杂表达式替换,这种方法不容易扩展。如果没有更好的答案出现,我会将其标记为已接受。 - jangorecki
1
我稍微修改了答案,以便在调用中有多个参数且params的第二个组件是名称向量时仍然有效。同时将其简化为单行。最后我们展示了对于问题的示例它给出了相同的答案。 - G. Grothendieck
谢谢。当尝试替换嵌套的命名元素my_fun(my_name1 = f(my_name2 = my_value))时,复杂性也会出现,但这显然不是问题的一部分。我在github上@了您,这样您就可以知道使用情况。 - jangorecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接