Step 1:决定什么是“数学运算符”。一种选择是从S4 泛型 https://stat.ethz.ch/R-manual/R-devel/library/methods/html/S4groupGeneric.html。例如,
mathOps <- unlist(lapply( c("Arith","Compare","Math"), getGroupMembers ))
# [1] "+" "-" "*" "^" "%%" "%/%"
# [7] "/" "==" ">" "<" "!=" "<="
# [13] ">=" "abs" "sign" "sqrt" "ceiling" "floor"
# [19] "trunc" "cummax" "cummin" "cumprod" "cumsum" "exp"
# [25] "expm1" "log" "log10" "log2" "log1p" "cos"
# [31] "cosh" "sin" "sinh" "tan" "tanh" "acos"
# [37] "acosh" "asin" "asinh" "atan" "atanh" "cospi"
# [43] "sinpi" "tanpi" "gamma" "lgamma" "digamma" "trigamma"
Step 2:将你的表达式分解为抽象语法树 https://en.wikipedia.org/wiki/Abstract_syntax_tree.
getAST <- function( ee )
lapply( as.list(ee), function(x) `if`(is.call(x), getAST(x), x) )
# Example usage
getAST( quote(sin(x+5)) )
# [[1]]
# sin
#
# [[2]]
# [[2]][[1]]
# `+`
#
# [[2]][[2]]
# x
#
# [[2]][[3]]
# [1] 5
Step 3:根据您对“有效性”的定义遍历 AST
checkFxn <- function( ast, validOps )
{
## Terminal nodes of an AST will not be lists
## Wrap them into a list of length 1 to keep the recursion flow
if( !is.list(ast) ) ast <- list(ast)
## Operators must be called with one or more arguments
if( as.character(ast[[1]]) %in% validOps )
return( `if`(length(ast) < 2, FALSE,
all(sapply(ast[-1], checkFxn, validOps))) )
## Variables x and m are OK
if( identical(ast[[1]], quote(x)) || identical(ast[[1]], quote(m)) )
return(TRUE)
## Constants are OK
if( is.numeric(ast[[1]]) ) return(TRUE)
## Everything else is invalid
FALSE
}
把它们放在一起
exprs <- lapply( list(s1,s2,s3,s4), str2lang ) # Convert strings to expressions
asts <- lapply( exprs, getAST ) # Build ASTs
sapply( asts, checkFxn, mathOps ) # Evaluate validity
# [1] TRUE TRUE FALSE FALSE
AST 的替代品
正如@Moody_Mudskipper 所指出的,人们还可以使用all.names
检索任意表达式中出现的符号列表。虽然这不会保留这些符号的相对结构,但可以直接将名称与mathOps
.