【问题标题】:base R substitute names of the arguments to function callbase R 将参数的名称替换为函数调用
【发布时间】:2020-06-20 13:57:28
【问题描述】:

问题的最终目标是使用 对语言的计算来构造以下未评估调用,其中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)

base 中是否有任何方法来构造所需的调用? 基本上可以得到与 rlang 完全相同的输出,但使用 base

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

【问题讨论】:

  • str2lang(paste("", "list", " (", "a_name", " = ", "50L", ")")) 能给你想要的吗?
  • @jay.sf str2lang 只是 parse 的特殊版本,所以并不比示例 3 好。

标签: r r r r r metaprogramming rlang


【解决方案1】:

假设问题是如何产生一个call对象,它的函数是params的第一个参数,它的单个参数名称是params的第二个分量的值,它的参数值是第三个分量params 然后 c2 是那个调用对象。我们验证生成的 cakl 对象与问题中显示的 rlang 调用对象相同。

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 并且params 的第二个组件是长度为 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]]))

【讨论】:

  • 感谢您的详细说明。同意,它确实通过替换元素的名称来工作。我希望在基本 R api 中有一种更简洁的方法,但我认为没有。将这种方法扩展到复杂的表达式替换并不容易。如果没有更好的答案出现,将标记为已接受。
  • 我已经稍微修改了答案,以便如果调用中有多个参数并且params 的第二个组件是名称向量,它仍然可以工作。也将其减少为单行。我们在最后显示,对于问题的示例,它给出了相同的答案。
  • 谢谢。尝试替换嵌套的命名元素my_fun(my_name1 = f(my_name2 = my_value)) 时也会出现复杂性,但这显然不是问题的一部分。我在 github 上 ping 了您,以便您了解用例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
  • 1970-01-01
  • 1970-01-01
  • 2023-01-09
  • 1970-01-01
  • 2019-05-10
  • 1970-01-01
相关资源
最近更新 更多