【问题标题】:R: Match parameters to expression tree from parse(text = )R:从 parse(text = ) 将参数匹配到表达式树
【发布时间】:2020-07-26 05:46:10
【问题描述】:

我正在阅读一些代码,需要确定所传递参数的参数名称。如果该人命名参数,我可以通过x[[param_name]] 找到它们。如果不明确,我如何找到它们?

在这个例子中,我试图找到“from”参数。我可以看到它是索引 #4,但它可能在位置 #2 或其他位置,并且它们中的任何一个或全部都可以是未命名的。

感谢您的宝贵时间。

x <- parse(text = "seq.int(to = 10, by = 2, 0)")

code_as_call <- as.call(x)[[1]]

View(code_as_call)

param_to <- code_as_call[["to"]]
param_by <- code_as_call[["by"]]
param_from <- "???" # how do I find this?

更新:

在@MichaelChirico 的帮助下,这是我想出的解决方案。我不得不考虑部分匹配作为一种选择。

arg_names <- function(x) {
  x_fn <- x[[1]][[3]]

  default_args <- c("", formalArgs(args(eval(x_fn[[1]]))))
  missing_names <- is.null(names(x_fn))

  seq_args <- seq_along(x_fn)
  #skip_last <- head(seq_args, -1)
  
  
  if (missing_names) {
    # assign names after first position
    names(x_fn) <- default_args[seq_args]
  } else {
    
    orig_args <- names(x_fn)
    has_name <- nchar(orig_args) > 0
    
    # line up args including partial matches
    explicit_args <- pmatch(orig_args[has_name], default_args)
    # update names
    names(x_fn)[which(has_name)] <- default_args[explicit_args]
    updated_args <- names(x_fn)
    
    # missing args
    avail_args <- setdiff(default_args, updated_args[has_name])
    missing_name <- which(!has_name)
    implicit_args <- avail_args[seq_along(missing_name)]
    # update names
    names(x_fn)[missing_name] <- implicit_args
  }
  
  names(x_fn)
}

arg_names(expression(new_string <- gsub(' ', '_', 'a b c')))
#> [1] ""            "pattern"     "replacement" "x"
arg_names(expression(new_string <- gsub(x = 'a b c', ' ', '_')))
#> [1] ""            "x"           "pattern"     "replacement"
arg_names(expression(new_string <- gsub(x = 'a b c', pat = ' ', rep = '_')))
#> [1] ""            "x"           "pattern"     "replacement"

reprex package (v0.3.0) 于 2020 年 7 月 26 日创建

【问题讨论】:

    标签: r rlang


    【解决方案1】:

    这不是一个完全通用的解决方案,但希望能说明所有必要的 base 辅助元编程功能,您需要到达那里。

    我们首先要获取可用参数的名称:

    xi = x[[1L]]
    
    all_args = setdiff(formalArgs(args(eval(xi[[1L]]))), '...')
    all_args
    # [1] "from"       "to"         "by"         "length.out" "along.with"
    

    注意:通常,我们可以直接使用formalArgs,但由于seq.intPrimitive 函数(is.primitive(seq.int)),formals 返回NULL——参见?formals,它推荐@987654330 @ 在这种情况下。

    然后,我们要看看实际调用中使用了什么:

    used_args = names(xi[-1L])
    # [1] "to" "by" ""  
    

    然后,我们按顺序从all_args 中获取参数在那些未命名的参数中

    avail_args = setdiff(all_args, used_args[has_name])
    implicit_args = avail_args[seq_along(used_args[!has_name])]
    implicit_args
    # [1] from
    

    【讨论】:

    • 感谢您抽出宝贵时间帮助我。你让我大部分时间到达那里,但由于部分匹配可用,它变得比我预期的要复杂。我将最终代码作为更新放入问题中。如果您有任何简化的建议,请告诉我。
    • @yake84 pmatch 就是为了这个。如果您可以控制代码,则还可以选择阻止部分参数匹配...
    【解决方案2】:

    这基本上就是match.call() 所做的:

    match.call 返回一个调用,其中所有指定的参数都由它们的全名指定。

    以下都是等效的使用方式:

    match.call( gsub, call("gsub", ' ', '_', 'a b c') )     
    # gsub(pattern = " ", replacement = "_", x = "a b c")
    
    code <- expression(gsub(x = 'a b c', pat = ' ', rep = '_'))
    match.call( gsub, code )
    # gsub(pattern = " ", replacement = "_", x = "a b c")
    
    code <- quote(gsub(x = 'a b c', pat = ' ', rep = '_'))
    match.call( gsub, as.call(code) )
    # gsub(pattern = " ", replacement = "_", x = "a b c")
    

    该函数不支持像seq.int 这样的原语,但您可以通过将函数名称包装在args() 中来欺骗它。这有效地构造了一个具有完全相同签名的新函数。

    match.call( seq.int, code_as_call )                   # Fails on primitives
    # Error in match.call(seq.int, code_as_call) : 
    #   invalid 'definition' argument
    
    match.call( args("seq.int"), code_as_call )           # Use the args() trick
    # seq.int(from = 0, to = 10, by = 2)
    

    【讨论】:

    • 感谢您的尝试。我不确定我是否遵循。你可以试试这个例子吗? code &lt;- expression(gsub(x = 'a b c', pat = ' ', rep = '_')) 然后match.call(args("gsub"), as.call(code))
    • @yake84 expression() 已经产生了一个调用,所以你不需要as.call()。或者,使用quote() 捕获未计算的表达式。我将您的示例纳入我的答案中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 2016-09-12
    相关资源
    最近更新 更多