评估包含另一个呼叫的呼叫(呼叫内的呼叫) [英] Evaluate call that contains another call (call within call)

查看:107
本文介绍了评估包含另一个呼叫的呼叫(呼叫内的呼叫)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一段代码,其中调用包含另一个调用.例如:

a <- 1
b <- 2
# First call
foo <- quote(a + a)
# Second call (call contains another call)
bar <- quote(foo ^ b)

我们可以使用eval(eval(foo))评估呼叫,但是eval(bar)无法正常工作.这是预期的,因为R尝试运行"foo" ^ 2(请参阅foo作为非数字对象).
如何评估这样的 callception ?

解决方案

要回答此问题,将其分为3个子问题可能会有所帮助

  1. 在通话中找到任何通话
  2. 对于每个呼叫,(不可见地)评估呼叫,将呼叫替换为原始呼叫
  3. 返回初始呼叫.

为使答案完整,我们需要在呼叫中找到任何随后嵌套的呼叫.另外,我们需要避免bar <- quote(bar + 3)的无限循环.

由于任何调用都可能嵌套,例如:

a <- 3
zz <- quote(a + 3)
foo <- quote(zz^a)
bar <- quote(foo^zz)

在评估最终调用之前,我们必须确保评估了每个堆栈.

按照这种思路,以下函数将评估甚至是复杂的调用.

eval_throughout <- function(x, envir = NULL){
  if(!is.call(x))
    stop("X must be a call!")

  if(isNullEnvir <- is.null(envir))
    envir <- environment()
  #At the first call decide the environment to evaluate each expression in (standard, global environment)
  #Evaluate each part of the initial call, replace the call with its evaluated value
  # If we encounter a call within the call, evaluate this throughout.
  for(i in seq_along(x)){
    new_xi <- tryCatch(eval(x[[i]], envir = envir),
                       error = function(e)
                         tryCatch(get(x[[i]],envir = envir), 
                                  error = function(e)
                                    eval_throughout(x[[i]], envir)))
    #Test for endless call stacks. (Avoiding primitives, and none call errors)
    if(!is.primitive(new_xi) && is.call(new_xi) && any(grepl(deparse(x[[i]]), new_xi)))
      stop("The call or subpart of the call is nesting itself (eg: x = x + 3). ")
    #Overwrite the old value, either with the evaluated call, 
    if(!is.null(new_xi))
      x[[i]] <- 
        if(is.call(new_xi)){
          eval_throughout(new_xi, envir)
        }else
          new_xi
  }
  #Evaluate the final call
  eval(x)
}

展示柜

因此,让我们尝试一些示例.最初,我将使用问题中的示例,再进行一个稍微复杂一些的调用.

a <- 1
b <- 2
c <- 3
foo <- quote(a + a)
bar <- quote(foo ^ b)
zz <- quote(bar + c) 

评估其中的每一个都可以得到理想的结果:

>eval_throughout(foo)
2
>eval_throughout(bar)
4
>eval_throughout(zz)
7

但是,这不限于简单的调用.让我们将其扩展到更有趣的通话中.

massive_call <- quote({
  set.seed(1)
  a <- 2
  dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
  names(dat) <- c("A","B")
  fit <- lm(A~B, data = dat)
  diff(coef(fit)) + 3 + foo^bar / (zz^bar)
})

令人惊讶的是,这样做也很好.

>eval_throughout(massive_call)
B
4

当我们尝试仅评估实际必要的细分时,我们得到的结果是相同的:

>set.seed(1)
>a <- 2
>dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
>names(dat) <- c("A","B")
>fit <- lm(A~B, data = dat)
>diff(coef(fit)) + 3 + eval_throughout(quote(foo^bar / (zz^bar)))
B
4

请注意,这可能不是最有效的评估方案.最初,envir变量应为NULL,除非应评估dat <- x之类的调用并将其保存在特定环境中.


当前提供的答案摘要和性能概述

自从获得了额外的奖励以来,这个问题已经引起了相当多的关注,并且提出了许多不同的答案.在本节中,我将简要概述答案,它们的局限性以及它们的一些好处.请注意,当前提供的所有答案都是不错的选择,但是以不同的程度,具有不同的优势和不利条件来解决问题.因此,本部分并不意味着对任何答案都给出负面评价,而只是尝试对不同方法进行概述. 在我的答案中上面给出的示例已被其他一些答案所采用,而在此答案的注释中已经提出了一些示例,它们代表了问题的不同方面.我将在我的答案以及下面的一些例子中使用这些示例,以尝试说明本文中建议的不同方法的有用性.为了完成,下面的代码显示了不同的示例.感谢@Moody_Mudskipper提供了以下注释中建议的其他示例!

#Example 1-4:
a <- 1
b <- 2
c <- 3
foo <- quote(a + a)
bar <- quote(foo ^ b)
zz <- quote(bar + c) 
massive_call <- quote({
  set.seed(1)
  a <- 2
  dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
  names(dat) <- c("A","B")
  fit <- lm(A~B, data = dat)
  diff(coef(fit)) + 3 + foo^bar / (zz^bar)
})
#Example 5
baz <- 1
quz <- quote(if(TRUE) baz else stop())
#Example 6 (Endless recursion)
ball <- quote(ball + 3)
#Example 7 (x undefined)
zaz <- quote(x > 3)


解决方案的多功能性

问题答案中提供的解决方案,广泛地解决了问题.一个问题可能是这些扩展到哪一类来解决评估带引号的表达式的各种任务. 为了测试解决方案的多功能性,使用每个答案中提供的 raw 函数对示例1至5进行了评估.实施例6图7中存在不同类型的问题,将在下面的部分(实施安全)中单独进行处理.请注意,oshka::expand返回一个未求值的表达式,该表达式在运行函数调用后进行了求值. 在下表中,我已经可视化了多功能测试的结果.在回答问题时,每一行都是一个单独的函数,而每一列则是一个示例.对于每个测试,成功分别标记为成功错误失败,分别表示成功,早期中断和失败评估. (代码可在答案的结尾处获得,以实现可重复性.)

            function     bar     foo  massive_call     quz      zz
1:   eval_throughout  succes  succes        succes   ERROR  succes
2:       evalception  succes  succes         ERROR   ERROR  succes
3:               fun  succes  succes         ERROR  succes  succes
4:     oshka::expand  sucess  sucess        sucess  sucess  sucess
5: replace_with_eval  sucess  sucess         ERROR   ERROR   ERROR

有趣的是,较简单的调用barfoozz大部分由一个答案处理.只有oshka::expand成功评估每种方法. massive_callquz示例仅继承两个方法,而对于特别讨厌的条件语句,只有oshka::expand成功评估表达式. 但是,可能有人注意到,通过设计,使用oshka::expand方法可以保存任何中间结果,使用时应牢记这些中间结果.但是,可以通过评估函数或子环境中全局环境中的表达式来简单地解决此问题. 另一个重要说明是,第5个示例代表了大多数答案中的特殊问题.由于每个表达式都是在5个答案中的3个中分别求值的,因此对stop函数的调用只会中断该调用.因此,任何包含对stop的调用的带引号的表达式都将显示一个简单且特别引人注意的示例.


效率比较:

经常要关注的替代性能衡量指标是纯粹的效率或速度.即使某些方法失败了,但要意识到方法的局限性,也会因速度性能而产生一种情况,即采用更简单的方法更好. 为了比较这些方法,我们需要假设情况是我们知道该方法足以解决我们的问题.因此,为了比较不同的方法,使用zz作为标准进行了基准测试.这消除了一种方法,该方法尚未执行基准测试.结果如下所示.

Unit: microseconds
            expr      min        lq       mean    median        uq      max neval
 eval_throughout  128.378  141.5935  170.06306  152.9205  190.3010  403.635   100
     evalception   44.177   46.8200   55.83349   49.4635   57.5815  125.735   100
             fun   75.894   88.5430  110.96032   98.7385  127.0565  260.909   100
    oshka_expand 1638.325 1671.5515 2033.30476 1835.8000 1964.5545 5982.017   100

出于比较的目的,中位数是更好的估计,因为垃圾清理器可能会污染某些结果,因此会污染均值. 从输出中可以看到清晰的图案.更高级的功能需要花费更长的时间进行评估. 在四个功能中,oshka::expand是最慢的竞争对手,比最接近的竞争对手慢18倍(1835.8/152.9 = 12),而evalception最快是大约fun(98.7/49.5 = 2)并且比eval_throughout快三倍(该死!) 因此,如果需要速度,似乎最简单的评估成功的方法就是路要走.


实施安全性 良好实现的一个重要方面是其识别和处理不正确输入的能力.对于此方面,示例6& amp;图7代表不同的问题,可能会破坏实现.示例6表示无限递归,这可能会中断R会话.示例7表示缺失值问题.

示例6 是在相同条件下运行的.结果如下所示.

eval_throughout(ball) #Stops successfully
eval(oshka::expand(ball)) #Stops succesfully
fun(ball) #Stops succesfully
#Do not run below code! Endless recursion
evalception(ball)

在四个答案中,只有evalception(bar)无法检测到无限递归,并且使R会话崩溃,而其余成功停止.

注意:我不建议运行后一个示例.

示例7 在相同条件下运行.结果如下所示.

eval_throughout(zaz) #fails
oshka::expand(zaz) #succesfully evaluates
fun(zaz) #fails
evalception(zaz) #fails

重要的一点是,对示例7的任何评估都将失败.只有oshka::expand成功,因为它旨在使用基础环境将任何现有值插入表达式中.这一特别有用的功能使您可以创建复杂的调用并插值任何带引号的表达式以扩展该表达式,而其余答案(包括我自己的答案)在评估表达式时会因设计而失败.


最终评论

所以你去了.我希望对答案的总结证明是有用的,以显示每种实现的正面和负面影响.每一种都有其可能胜过其他方案的可能方案,而在所代表的所有情况下只有一种方案可以成功使用. 对于多功能性,oshka::expand是明显的赢家,而如果希望使用速度,则必须评估答案是否可以用于当前情况.通过使用更简单的答案可以实现极大的速度改进,而它们代表了可能导致R会话崩溃的不同风险.与我之前的摘要不同,读者可以自己决定哪种实现方式最适合其特定问题.

用于复制摘要的代码

请注意,此代码未清除,只需汇总即可.此外,它不包含示例或函数,仅包含它们的评估.

require(data.table)
require(oshka)
evals <- function(fun, quotedstuff, output_val, epsilon = sqrt(.Machine$double.eps)){
  fun <- if(fun != "oshka::expand"){
    get(fun, env = globalenv())
  }else
    oshka::expand
  quotedstuff <- get(quotedstuff, env = globalenv())
  output <- tryCatch(ifelse(fun(quotedstuff) - output_val < epsilon, "succes", "failed"), 
                     error = function(e){
                       return("ERROR")
                     })
  output
}
call_table <- data.table(CJ(example = c("foo", 
                                        "bar", 
                                        "zz", 
                                        "massive_call",
                                        "quz"),
                            `function` = c("eval_throughout",
                                           "fun",
                                           "evalception",
                                           "replace_with_eval",
                                           "oshka::expand")))
call_table[, incalls := paste0(`function`,"(",example,")")]
call_table[, output_val := switch(example, "foo" = 2, "bar" = 4, "zz" = 7, "quz" = 1, "massive_call" = 4), 
           by = .(example, `function`)]
call_table[, versatility := evals(`function`, example, output_val), 
           by = .(example, `function`)]
#some calls failed that, try once more
fun(foo)
fun(bar) #suces
fun(zz) #succes
fun(massive_call) #error
fun(quz)
fun(zaz)
eval(expand(foo)) #success
eval(expand(bar)) #sucess
eval(expand(zz)) #sucess
eval(expand(massive_call)) #succes (but overwrites environment)
eval(expand(quz))
replace_with_eval(foo, a) #sucess
replace_with_eval(bar, foo) #sucess
replace_with_eval(zz, bar) #error
evalception(zaz)
#Overwrite incorrect values.
call_table[`function` == "fun" & example %in% c("bar", "zz"), versatility := "succes"]
call_table[`function` == "oshka::expand", versatility := "sucess"]
call_table[`function` == "replace_with_eval" & example %in% c("bar","foo"), versatility := "sucess"]
dcast(call_table, `function` ~ example, value.var = "versatility")
require(microbenchmark)
microbenchmark(eval_throughout = eval_throughout(zz),
               evalception = evalception(zz),
               fun = fun(zz),
               oshka_expand = eval(oshka::expand(zz)))
microbenchmark(eval_throughout = eval_throughout(massive_call),
               oshka_expand = eval(oshka::expand(massive_call)))
ball <- quote(ball + 3)
eval_throughout(ball) #Stops successfully
eval(oshka::expand(ball)) #Stops succesfully
fun(ball) #Stops succesfully
#Do not run below code! Endless recursion
evalception(ball)
baz <- 1
quz <- quote(if(TRUE) baz else stop())
zaz <- quote(x > 3)
eval_throughout(zaz) #fails
oshka::expand(zaz) #succesfully evaluates
fun(zaz) #fails
evalception(zaz) #fails

I have encountered a snippet of code where call contains another call. For example:

a <- 1
b <- 2
# First call
foo <- quote(a + a)
# Second call (call contains another call)
bar <- quote(foo ^ b)

We can evaluate calls with eval (eval(foo)), however eval(bar) won't work. This is expected as R tries to run "foo" ^ 2 (sees foo as non-numeric object).
How to evaluate such callception?

解决方案

To answer this question it might be helpful to split it up in 3 sub problems

  1. Locate any call within a call
  2. For each call, evaluate the call (invisibly), or replace the call with the original call
  3. Return the initial call.

For the answer to be complete, we need to locate any subsequently nested call within the call. In addition we would need to avoid the endless loop of bar <- quote(bar + 3).

As any call might have nested called eg:

a <- 3
zz <- quote(a + 3)
foo <- quote(zz^a)
bar <- quote(foo^zz)

we will have to make sure each stack is evaluated before evaluating the final call.

Following this line of thought, the following function will evaluate even complicated calls.

eval_throughout <- function(x, envir = NULL){
  if(!is.call(x))
    stop("X must be a call!")

  if(isNullEnvir <- is.null(envir))
    envir <- environment()
  #At the first call decide the environment to evaluate each expression in (standard, global environment)
  #Evaluate each part of the initial call, replace the call with its evaluated value
  # If we encounter a call within the call, evaluate this throughout.
  for(i in seq_along(x)){
    new_xi <- tryCatch(eval(x[[i]], envir = envir),
                       error = function(e)
                         tryCatch(get(x[[i]],envir = envir), 
                                  error = function(e)
                                    eval_throughout(x[[i]], envir)))
    #Test for endless call stacks. (Avoiding primitives, and none call errors)
    if(!is.primitive(new_xi) && is.call(new_xi) && any(grepl(deparse(x[[i]]), new_xi)))
      stop("The call or subpart of the call is nesting itself (eg: x = x + 3). ")
    #Overwrite the old value, either with the evaluated call, 
    if(!is.null(new_xi))
      x[[i]] <- 
        if(is.call(new_xi)){
          eval_throughout(new_xi, envir)
        }else
          new_xi
  }
  #Evaluate the final call
  eval(x)
}

Showcase

So lets try a few examples. Initially I'll use the example in the question, with one additional slightly more complicated call.

a <- 1
b <- 2
c <- 3
foo <- quote(a + a)
bar <- quote(foo ^ b)
zz <- quote(bar + c) 

Evaluating each of these gives the desired result:

>eval_throughout(foo)
2
>eval_throughout(bar)
4
>eval_throughout(zz)
7

This is not restricted to simple calls however. Lets extend it to a more interesting call.

massive_call <- quote({
  set.seed(1)
  a <- 2
  dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
  names(dat) <- c("A","B")
  fit <- lm(A~B, data = dat)
  diff(coef(fit)) + 3 + foo^bar / (zz^bar)
})

Suprisingly enough this also works out just fine.

>eval_throughout(massive_call)
B
4

as when we try to evaluate only the segment that is actually necessary, we get the same result:

>set.seed(1)
>a <- 2
>dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
>names(dat) <- c("A","B")
>fit <- lm(A~B, data = dat)
>diff(coef(fit)) + 3 + eval_throughout(quote(foo^bar / (zz^bar)))
B
4

Note that this is likely not the most efficient evaluating scheme. Initially the envir variable should be NULL, unless calls like dat <- x should be evaluated and saved in a specific environment.


Edit: Summary of currently provided answers and performance overview

This question have been given quite some attention since the additional reward was given, and many different answers have been proposed. In this section I'll give a short overview of the answers, their limitations and some of their benefits as well. Note all the answers currently provided are good options, but solve the problem to a differing degree, with different upsides and downsides. This section is thus not meant as a negative review for any of the answers, but a trial to leave an overview of the different methods. The examples presented in above in my answer have been adopted by some of the other answers, while a few have been suggested in the comments of this answer which represented different aspects of the problem. I will use the examples in my answer as well as a few below, to try and illustrate the usefulness of the different methods suggested throughout this post. For completion the different examples are shown in code below. Thanks to @Moody_Mudskipper for the additional examples suggested in the comments below!

#Example 1-4:
a <- 1
b <- 2
c <- 3
foo <- quote(a + a)
bar <- quote(foo ^ b)
zz <- quote(bar + c) 
massive_call <- quote({
  set.seed(1)
  a <- 2
  dat <- data.frame(MASS::mvrnorm(n = 200, mu = c(3,7), Sigma = matrix(c(2,4,4,8), ncol = 2), empirical = TRUE))
  names(dat) <- c("A","B")
  fit <- lm(A~B, data = dat)
  diff(coef(fit)) + 3 + foo^bar / (zz^bar)
})
#Example 5
baz <- 1
quz <- quote(if(TRUE) baz else stop())
#Example 6 (Endless recursion)
ball <- quote(ball + 3)
#Example 7 (x undefined)
zaz <- quote(x > 3)


Solution versatility

The solutions provided in the answers to the question, solve the problem to various extends. One question might be to which extend these solve the various tasks of evaluating the quoted expressions. To test the versatility of the solutions, example 1 to 5 was evaluated using the raw function provided in each answer. Example 6 & 7 present different kind of problems, and will be treated seperately in a section below (Safety of Implementation). Note the oshka::expand returns an unevaluated expression, which was evaluated for after running the function call. In the table below I've visualized the results from the versatility test. Each row is a seperate function in an answer to the question while each column marks an example. For each test the succes is marked as sucess, ERROR and failed for a succesfuly, early interrupted and failed evaluation respectively. (Codes are availible at the end of the answer for reproducability.)

            function     bar     foo  massive_call     quz      zz
1:   eval_throughout  succes  succes        succes   ERROR  succes
2:       evalception  succes  succes         ERROR   ERROR  succes
3:               fun  succes  succes         ERROR  succes  succes
4:     oshka::expand  sucess  sucess        sucess  sucess  sucess
5: replace_with_eval  sucess  sucess         ERROR   ERROR   ERROR

Interestingly the simpler calls bar, foo and zz are mostly handled by all but one answer. Only oshka::expand succesfuly evaluates every method. Only two methods succeed the massive_call and quz examples, while only oshka::expand craetes a succesfuly evaluating expression for the particularly nasty conditional statement. One may however note that by design the any intermediate results are saved using the oshka::expand method, which should be kept in mind while used. This could however be simply fixed by evaluating the expression within function or child-environment to the global environment. Another important note is the 5'th example represents a special problem with most of the answers. As each expression is evaluated individually in 3 out of 5 answers, the call to the stop function, simply breaks the call. Thus any quoted expression containing a call to stop shows a simply and especially devious example.


Efficiency comparison:

An alternative performance meassure often of concern is pure efficiency or speed. Even if certain methods failed, being aware of the methods limitations, can yield situations where a simpler method is better, due to the speed performance. To compare the methods we need to assume that it is the case that we know the method is sufficient for our problems. For this reason and in order to compare the different methods a benchmarking test was performed using zz as the standard. This cuts out one method, for which no benchmarking has been performed. The results are shown below.

Unit: microseconds
            expr      min        lq       mean    median        uq      max neval
 eval_throughout  128.378  141.5935  170.06306  152.9205  190.3010  403.635   100
     evalception   44.177   46.8200   55.83349   49.4635   57.5815  125.735   100
             fun   75.894   88.5430  110.96032   98.7385  127.0565  260.909   100
    oshka_expand 1638.325 1671.5515 2033.30476 1835.8000 1964.5545 5982.017   100

For the purposes of comparison, the median is a better estimate, as the garbage cleaner might taint certain results and thus the mean. From the output a clear pattern is visible. The more advanced functions takes longer to evaluate. Of the four functions oshka::expand is the slowest competitor, being a factor 12 slower than the closest competitor (1835.8 / 152.9 = 12), while evalception is the fastest being about twice as fast as fun (98.7 / 49.5 = 2) and three times faster than eval_throughout (damn!) As such if speed is required, it seems the simplest method that will evaluate succesfuly is the way to go.


Safety of implementation An important aspect of good implementations is their ability identify and handle devious input. For this aspect example 6 & 7 represent different problems, that could break implementations. Example 6 represents an endless recursion, which might break the R session. Example 7 represents the missing value problem.

Example 6 was run under the same condition. The results are shown below.

eval_throughout(ball) #Stops successfully
eval(oshka::expand(ball)) #Stops succesfully
fun(ball) #Stops succesfully
#Do not run below code! Endless recursion
evalception(ball)

Of the four answer, only evalception(bar) fails to detect the endless recursion, and crashes the R session, while the remaining succesfuly stops.

Note: i do not suggest running the latter example.

Example 7 was run under the same condition. The results are shown below.

eval_throughout(zaz) #fails
oshka::expand(zaz) #succesfully evaluates
fun(zaz) #fails
evalception(zaz) #fails

An important note is that any evaluation of example 7 will fail. Only oshka::expand succeeds, as it is designed to impute any existing value into the expression using the underlying environment. This especially useful feature lets one create complex calls and imputing any quoted expression to expand the expression, while the remaining answers (including my own) fail by design, as they evaluate the expression.


Final comments

So there you go. I hope the summary of the answers proves useful, showing the positives and possible negatives of each implementation. Each have their possible scenarios where they would outperform the remaining, while only one could be successfully used in all of the represented circumstances. For versatility the oshka::expand is the clear winner, while if speed is preferred one would have to evaluate if the answers could be used for the situation at hand. Great speed improvements is achievable by going with the simpler answers, while they represent different risks possibly crashing the R session. Unlike my earlier summary, the reader is left to decide for themselves which implementation would work best for their specific problem.

Code for reproducing the summary

Note this code is not cleaned, simply put together for the summary. In addition it does not contain the examples or function, only their evaluations.

require(data.table)
require(oshka)
evals <- function(fun, quotedstuff, output_val, epsilon = sqrt(.Machine$double.eps)){
  fun <- if(fun != "oshka::expand"){
    get(fun, env = globalenv())
  }else
    oshka::expand
  quotedstuff <- get(quotedstuff, env = globalenv())
  output <- tryCatch(ifelse(fun(quotedstuff) - output_val < epsilon, "succes", "failed"), 
                     error = function(e){
                       return("ERROR")
                     })
  output
}
call_table <- data.table(CJ(example = c("foo", 
                                        "bar", 
                                        "zz", 
                                        "massive_call",
                                        "quz"),
                            `function` = c("eval_throughout",
                                           "fun",
                                           "evalception",
                                           "replace_with_eval",
                                           "oshka::expand")))
call_table[, incalls := paste0(`function`,"(",example,")")]
call_table[, output_val := switch(example, "foo" = 2, "bar" = 4, "zz" = 7, "quz" = 1, "massive_call" = 4), 
           by = .(example, `function`)]
call_table[, versatility := evals(`function`, example, output_val), 
           by = .(example, `function`)]
#some calls failed that, try once more
fun(foo)
fun(bar) #suces
fun(zz) #succes
fun(massive_call) #error
fun(quz)
fun(zaz)
eval(expand(foo)) #success
eval(expand(bar)) #sucess
eval(expand(zz)) #sucess
eval(expand(massive_call)) #succes (but overwrites environment)
eval(expand(quz))
replace_with_eval(foo, a) #sucess
replace_with_eval(bar, foo) #sucess
replace_with_eval(zz, bar) #error
evalception(zaz)
#Overwrite incorrect values.
call_table[`function` == "fun" & example %in% c("bar", "zz"), versatility := "succes"]
call_table[`function` == "oshka::expand", versatility := "sucess"]
call_table[`function` == "replace_with_eval" & example %in% c("bar","foo"), versatility := "sucess"]
dcast(call_table, `function` ~ example, value.var = "versatility")
require(microbenchmark)
microbenchmark(eval_throughout = eval_throughout(zz),
               evalception = evalception(zz),
               fun = fun(zz),
               oshka_expand = eval(oshka::expand(zz)))
microbenchmark(eval_throughout = eval_throughout(massive_call),
               oshka_expand = eval(oshka::expand(massive_call)))
ball <- quote(ball + 3)
eval_throughout(ball) #Stops successfully
eval(oshka::expand(ball)) #Stops succesfully
fun(ball) #Stops succesfully
#Do not run below code! Endless recursion
evalception(ball)
baz <- 1
quz <- quote(if(TRUE) baz else stop())
zaz <- quote(x > 3)
eval_throughout(zaz) #fails
oshka::expand(zaz) #succesfully evaluates
fun(zaz) #fails
evalception(zaz) #fails

这篇关于评估包含另一个呼叫的呼叫(呼叫内的呼叫)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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