简体   繁体   English

更快的替代 deparse()

[英]Faster alternative to deparse()

I maintain a package that relies on repeated calls to deparse(control = c("keepNA", "keepInteger")) .我维护一个依赖于重复调用deparse(control = c("keepNA", "keepInteger")) control is always the same, and the expression varies. control总是相同的,表达方式各不相同。 deparse() seems to spend a lot of time repeatedly interpreting the same set of options with .deparseOpts() . deparse()似乎花了很多时间用.deparseOpts()重复解释同一组选项。

microbenchmark::microbenchmark(
    a = deparse(identity, control = c("keepNA", "keepInteger")),
    b = .deparseOpts(c("keepNA", "keepInteger"))
)
# Unit: microseconds
# expr min  lq  mean median  uq  max neval
#    a 7.2 7.4 8.020    7.5 7.6 55.1   100
#    b 3.0 3.2 3.387    3.4 3.5  6.0   100

On some systems, redundant .deparseOpts() calls actually take up the majority of the runtime of deparse() ( flame graph here ).在某些系统中,冗余.deparseOpts()调用实际占用大部分运行的deparse() 火焰图形这里)。

I would really like to just call .deparseOpts() once and then supply the numeric code to deparse() , but that appears impossible without calling .Internal() or invoking the C code directly, neither of which is optimal from a package development perspective.我真的很想只调用.deparseOpts()一次,然后将数字代码提供给deparse() ,但如果不调用.Internal()或直接调用 C 代码,这似乎是不可能的,从包开发的角度来看,这两者都不是最佳的.

deparse
# function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
#     c("call", "expression", "(", "function"), 
#     control = c("keepNA", "keepInteger", "niceNames", 
#         "showAttributes"), nlines = -1L) 
# .Internal(deparse(expr, width.cutoff, backtick, .deparseOpts(control), 
#     nlines))
# <bytecode: 0x0000000006ac27b8>
# <environment: namespace:base>

Is there a convenient workaround?有没有方便的解决方法?

1) Define a function which generates a copy of deparse whose environment has been reset to find an altered version of .deparseOpts which has been set to be equal to the identity function. 1)定义一个函数,该函数生成 deparse 的副本,其环境已重置以找到 .deparseOpts 的更改版本,该版本已设置为等于身份函数。 In Run we then run that function to create a deparse2 and execute that.Run我们然后运行该函数来创建一个deparse2并执行它。 This avoids running .Internal directly.这避免了直接运行.Internal

make_deparse <- function() {
  .deparseOpts <- identity
  environment(deparse) <- environment()
  deparse
}

Run <- function() {
  deparse2 <- make_deparse()
  deparse2(identity, control = 65)
}

# test
Run()

2) Another way to do this is to define a constructor function which creates an environment in which to put a modified copy of deparse and add a trace to that copy redefining .deparseOpts as the identity function. 2)另一种方法是定义一个构造函数,它创建一个环境,在其中放置deparse的修改副本并向该副本添加跟踪,将.deparseOpts重新定义为标识函数。 Then return that environment.然后返回那个环境。 We then have some function which uses it and for this example we create a function Run to demonstrate it and then just execute Run .然后我们有一些使用它的函数,对于这个例子,我们创建一个函数Run来演示它,然后只执行Run This avoids having to use .Internal这避免了必须使用.Internal

make_deparse_env <- function() {
  e <- environment()
  deparse <- deparse
  suppressMessages(
    trace("deparse", quote(.deparseOpts <- identity), print = FALSE, where = e)
  )
  e
}

Run <- function() {
  e <- make_deparse_env()
  e$deparse(identity, control = 65)
}

# test
Run()

3) A third approach is to redefine deparse by adding a new argument which sets .deparseOpts to have a default of identity and sets the control to have a default of 65. 3)第三种方法是通过添加一个新参数来重新定义deparse ,该参数将.deparseOpts设置为具有默认identity并将control设置为具有默认值 65。

make_deparse65 <- function() {
  deparse2 <- function (expr, width.cutoff = 60L, backtick = mode(expr) %in% 
    c("call", "expression", "(", "function"), 
    control = 65, nlines = -1L, .deparseOpts = identity) {}
  body(deparse2) <- body(deparse)
  deparse2
}

Run <- function() {
  deparse65 <- make_deparse65()
  deparse65(identity)
}

# test
Run()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM