R-使用非标准评估来检查字符串是否为有效的数学表达式 [英] R - Checking if a string is a valid mathematical expression using non-standard evaluation

查看:60
本文介绍了R-使用非标准评估来检查字符串是否为有效的数学表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想检查下面的字符串是否是有效的数学表达式:

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)'

有效"是指表达式是

  1. 运算符(必须与变量或常量结合使用)
  2. 变量 x 和/或 m
  3. 常量.

根据此定义, s1 s2 有效,而 s3 s4 无效.

By this definition s1 and s2 are valid while s3 and s4 are not.

要确定字符串是否有效,我编写了一个函数 checkFxn ,该函数首先尝试将字符串转换为调用或调用的一部分.如果成功,则它将遍历调用树并检查上述条件.如果满足条件,则按原样返回呼叫.如果没有,则抛出错误.

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>

它似乎可以按预期工作,但是我对使用 eval 表示谨慎,并想知道是否有人可以建议使用它吗?我知道它遵循通常的词法作用域规则,因此我担心它会在全局环境中评估变量-有没有办法限制其范围?我已经阅读了非标准一章评估,但我不知道.

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.

还有,有没有办法确定基本函数或基元是数学运算符?我想使用比 is.function is.primitive 更具体的东西.

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.

推荐答案

步骤1:确定什么构成数学运算符".一种选择是从 S4中检索相关的组仿制药.例如,

Step 1: Decide what constitutes a "mathematical operator". One option is to retrieve relevant groups from the S4 generics. For example,

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"

步骤2:将表达式分解为抽象语法树

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

第3步:根据您对有效性"的定义遍历AST

Step 3: Traverse the ASTs based on your definition of "validity"

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 进行比较.

As pointed out by @Moody_Mudskipper, one can also use all.names to retrieve the list of symbols occurring inside an arbitrary expression. While this doesn't preserve the relative structure of those symbols, the names can be compared directly against mathOps.

这篇关于R-使用非标准评估来检查字符串是否为有效的数学表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆