给定一个算术表达式,例如
x + y*z
,我想将其转换为 add(x, multiply(y, z))
。
我在这里找到了一个有用的功能:
> getAST <- function(ee) purrr::map_if(as.list(ee), is.call, getAST)
> getAST(quote(x + y*z))
[[1]]
`+`
[[2]]
x
[[3]]
[[3]][[1]]
`*`
[[3]][[2]]
y
[[3]][[3]]
z
可以使用
rapply(result, as.character, how = "list")
来获取字符而不是符号。
如何从这个 AST(结果)中得到
add(x, multiply(y, z))
?当有一些括号时,这会变得更加复杂:
> getAST(quote((x + y) * z))
[[1]]
`*`
[[2]]
[[2]][[1]]
`(`
[[2]][[2]]
[[2]][[2]][[1]]
`+`
[[2]][[2]][[2]]
x
[[2]][[2]][[3]]
y
[[3]]
z
我不要求答案必须使用
getAST
功能。这只是一种可行的方法。
当然,在我的实际用例中,表达式更长。
这是没有括号时的解决方案(我认为):
getAST <- function(ee) purrr::map_if(as.list(ee), is.call, getAST)
ast <- rapply(getAST(quote(x + y*z)), as.character, how = "list")
convertAST <- function(ast) {
op <- switch(
ast[[1]],
"+" = "add",
"-" = "subtract",
"*" = "multiply",
"/" = "divide"
)
left <- ast[[2]]
right <- ast[[3]]
if(is.character(left) && is.character(right)) {
return(sprintf("%s(%s, %s)", op, left, right))
}
if(is.character(left)) {
return(sprintf("%s(%s, %s)", op, left, convertAST(right)))
}
if(is.character(right)) {
return(sprintf("%s(%s, %s)", op, convertAST(left), right))
}
return(sprintf("%s(%s, %s)", op, convertAST(left), convertAST(right)))
}
convertAST(ast)
这可能只是因为我不太理解
rapply
,但任何时候我尝试使用它时,我的代码都比仅仅编写自己的递归函数更复杂。
在本例中,我将递归函数放入一个薄包装器中,该包装器允许直接输入表达式,而无需使用
quote
(如果需要)
sub_call <- function(input, direct = TRUE,
subs = list(`+` = "add", `-` = "minus",
`/` = "divide", `*` = "multiply")) {
scall <- function(x, subs) {
if(is.call(x))
{
if(as.character(x[[1]]) %in% names(subs)) {
x[[1]] <- str2lang(subs[[match(as.character(x[[1]]), names(subs))]])
}
}
if(length(x) == 1) return(x)
x[-1] <- lapply(x[-1], scall, subs = subs)
x
}
if(direct) return(scall(as.list(match.call())$input, subs))
return(scall(input, subs))
}
这允许直接输入表达式:
sub_call(x + y*z)
#> add(x, multiply(y, z))
或间接输入:
my_expr <- quote(x + y*z)
sub_call(my_expr, direct = FALSE)
#> add(x, multiply(y, z))
并处理任意深度的嵌套,保持括号完整:
sub_call(sin(((x + (1/3))^2)))
#> sin(((add(x, (divide(1, 3))))^2))