I would like to check if the strings below are valid mathematical expressions:
s1 = 'sin(x)'
s2 = 'sin(x*m)'
s3 = 'sin'
s4 = 'sin(xm)'
By 'valid', I mean the expression is a combination of
- operators (must be used in conjunction with variables or constants)
- variables
x
and/orm
- constants.
By this definition s1
and s2
are valid while s3
and s4
are not.
To identify if a string is valid, I wrote a function checkFxn
that first attempts to convert the string into a call or one of its parts. If successful, it then recurses through the call-tree and checks for the above conditions. If the conditions are satisfied, then the call is returned as-is. If not, an error is thrown.
checkFxn <- function(x) {
lang <- str2lang(x)
checkFxn2 <- function(y) {
if(is.name(y)) {
stopifnot(deparse(y) %in% c('x', 'm'))
} else if(is.call(y)) {
stopifnot(is.function(eval(y[[1]])) | is.primitive(eval(y[[1]])))
lapply(y[-1], checkFxn2)
} else {
stopifnot(is.logical(y) | is.numeric(y) | is.complex(y))
}
return(y)
}
checkFxn2(lang)
}
#Applying checkFxn to s1-4
lapply(list(s1,s2,s3,s4), function(x) {try(checkFxn(x), silent = T)})
[[1]]
sin(x)
[[2]]
sin(x * m)
[[3]]
[1] "Error in checkFxn2(lang) : deparse(y) %in% c(\"x\", \"m\") is not TRUE\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in checkFxn2(lang): deparse(y) %in% c("x", "m") is not TRUE>
[[4]]
[1] "Error in FUN(X[[i]], ...) : deparse(y) %in% c(\"x\", \"m\") is not TRUE\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in FUN(X[[i]], ...): deparse(y) %in% c("x", "m") is not TRUE>
It seems to work as expected but I'm wary of my use of eval
and was wondering if someone could suggest an alternative to using it? I know that it follows the usual lexical scoping rules, so I'm worried about it evaluating variables in the gobal environment - is there a way to restrict its scope? I've read the chapter on non-standard evaluation but I can't figure it out.
Also, is there a way to identify if a base function or primitive is a mathematical operator? I would like to use something more specific than is.function
and is.primitive
.