R:将参数与 parse(text = ) 中的表达式树匹配
R: Match parameters to expression tree from parse(text = )
我正在阅读一些代码,需要确定传递的参数的参数名称。如果有人命名参数,我可以用 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-07-26 创建
这不是一个完全通用的解决方案,但希望能说明所有必要的 base
帮助程序元编程功能,您将需要到达那里。
我们首先要获取可用参数的名称:
xi = x[[1L]]
all_args = setdiff(formalArgs(args(eval(xi[[1L]]))), '...')
all_args
# [1] "from" "to" "by" "length.out" "along.with"
注意:通常,我们可以直接使用 formalArgs
,但由于 seq.int
是一个 Primitive
函数 (is.primitive(seq.int)
),formals
returns NULL
-- 请参阅 ?formals
,在这种情况下建议使用 formals(args(.))
。
然后,我们要看看实际调用中使用了什么:
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
这基本上就是 match.call()
所做的:
match.call returns a call in which all of the specified arguments are specified by their full names.
以下都是等效的使用方式:
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)
我正在阅读一些代码,需要确定传递的参数的参数名称。如果有人命名参数,我可以用 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-07-26 创建这不是一个完全通用的解决方案,但希望能说明所有必要的 base
帮助程序元编程功能,您将需要到达那里。
我们首先要获取可用参数的名称:
xi = x[[1L]]
all_args = setdiff(formalArgs(args(eval(xi[[1L]]))), '...')
all_args
# [1] "from" "to" "by" "length.out" "along.with"
注意:通常,我们可以直接使用 formalArgs
,但由于 seq.int
是一个 Primitive
函数 (is.primitive(seq.int)
),formals
returns NULL
-- 请参阅 ?formals
,在这种情况下建议使用 formals(args(.))
。
然后,我们要看看实际调用中使用了什么:
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
这基本上就是 match.call()
所做的:
match.call returns a call in which all of the specified arguments are specified by their full names.
以下都是等效的使用方式:
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)