3

有类似的问题,我正在寻找比为这个问题提供的更通用的解决方案:如何替换语言对象中的符号?

我有一个未计算的表达式foo <- quote(bar + baz),它是其他表达式中的一个变量:qux <- quote(bla + foo)

我们可以检查:

exists("foo", envir=.GlobalEnv) & class(foo)=="call"
[1] TRUE
exists("qux", envir=.GlobalEnv) & class(qux)=="call"
[1] TRUE

现在我想编写一个通用函数,将(解析?)分解qux成组件表达式,并用它们的值替换存在于.GlobalEnv call中的那些:

replaceFUN <- function(x) {
  # do something
}

运行replaceFUN(qux)应该返回:

bla + (bar + baz)  

实际问题的背景:

我正在构建一个量化交易规则回测引擎。我的目标是延迟对 quote()d 表达式的评估,例如在定义之后的规则和指标计算。

require(data.table)
require(TTR) # for the `SMA` function

DT <- data.table(Instrument=rep("SPX",3),Date=1:3, Close=c(1050, 1052, 1051))

# define parameters
nSMA <-2
t <- 2

# define indicators
time.filter <- quote( Date==t )
moving.average <- quote( SMA(Close, nSMA) )    
buy <- quote( Close > moving.average & time.filter )


AddColumn <- function(x, colname) {
  DT[,eval(substitute(colname)):=eval(x, envir=.SD)]
}

AddColumn(time.filter, "filter")

    Instrument Date Close filter
 1:        SPX    1  1050  FALSE
 2:        SPX    2  1052   TRUE
 3:        SPX    3  1051  FALSE

AddColumn(moving.average, "MA")

   Instrument Date Close filter     MA
1:        SPX    1  1050  FALSE     NA
2:        SPX    2  1052   TRUE 1051.0
3:        SPX    3  1051  FALSE 1051.5

AddColumn(buy, "Buy")

Error in Close > moving.average & time.filter : 
operations are possible only for numeric, logical or complex types

这显然会引发错误,因为AddColumn函数缺少解析嵌套变量moving.averagetime.filter变量(以及用户定义和嵌套的任何其他变量)的机制。内部规则的嵌套buy是为了可读性,确实是一种语法糖。

4

2 回答 2

4

不久前,我正在解决一个与此非常相似的问题。查看源代码[.data.table并查看那里的anddeconstruct_and_eval函数construct。它应该为您提供足够的信息以继续进行。

于 2013-10-12T00:10:51.963 回答
3

[.data.table从源代码自定义此函数 会产生所需的通用解决方案:

deconstruct_and_eval = function(expr, envir = parent.frame(), enclos = parent.frame()) {

  if (!mode(expr) %in% c("call", "expression")) 
    return(expr)

  if (length(expr) == 1) {
    if (is.call(expr[[1]])) return (deconstruct_and_eval(expr[[1]]))
    else return(expr)
  }

  if (expr[[1]] == quote(eval) && length(expr) < 3) {
    return(deconstruct_and_eval(eval(expr[[2]], envir, enclos), envir, enclos))
  }

  lapply(expr, function(m) {
    if (is.call(m)) {
      if (m[[1]] == quote(eval)) eval(m[[2]], envir, enclos)
      else deconstruct_and_eval(m, envir, enclos)
    } else {
# begin edit 
      if(exists(as.character(m),envir=.GlobalEnv)) {
        if(!is.function(eval(m)))
           eval(m)
           else
             m
      } else
# end edit
        m
    }
  })
}

使用原始问题变量运行函数会产生:

deconstruct_and_eval(qux)
[[1]]
`+`

[[2]]
bla

[[3]]
bar + baz

然后可以使用源代码construct中的函数重建此解构列表:

construct(deconstruct_and_eval(qux))
bla + (bar + baz)

实际问题的应用:

deconstruct_and_eval(buy)
[[1]]
`&`

[[2]]
[[2]][[1]]
`>`

[[2]][[2]]
Close

[[2]][[3]]
SMA(Close, nSMA)


[[3]]
Date == t

construct(deconstruct_and_eval(buy))
Close > SMA(Close, nSMA) & Date == t
于 2013-10-13T16:06:07.300 回答