Evaluate call that contains another call (call within call)

后端 未结 5 1898
-上瘾入骨i
-上瘾入骨i 2021-02-19 06:41

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 c         


        
5条回答
  •  眼角桃花
    2021-02-19 07:36

    I think you might want :

    eval(do.call(substitute, list(bar, list(foo = foo))))
    # [1] 4
    

    The call before evaluation :

    do.call(substitute, list(bar, list(foo = foo)))
    #(a + a)^b
    

    This also works and might be easier to understand:

    eval(eval(substitute(
      substitute(bar, list(foo=foo)),
      list(bar = bar))))
    # [1] 4
    

    and going backwards :

    eval(substitute(
      substitute(bar, list(foo=foo)), 
      list(bar = bar)))
    # (a + a)^b
    

    And some more

    substitute(
      substitute(bar, list(foo=foo)),
      list(bar = bar))
    # substitute(foo^b, list(foo = foo))
    

    Not completely the same but you could use bquote here too if you can afford to define bar differently :

    bar2 <- bquote(.(foo)^b)
    bar2
    # (a + a)^b
    eval(bar2)
    # [1] 4
    

    And in that case the close equivalent using rlang will be :

    library(rlang)
    foo <- expr(a + a) # same as quote(a + a)
    bar2 <- expr((!!foo) ^ b)
    bar2
    # (a + a)^b
    eval(bar2)
    # [1] 4
    

    And a minor thing, you say :

    This is expected as R tries to run "foo" ^ 2

    It doesn't, it tries to run quote(foo)^b , which will return this same error if you run it directly in the console.


    Addendum on recursion

    Borrowing Oliver's example you can deal with recursion by looping on my solution until you've evaluated all you can, we just have to slightly modifiy our substitute call to provide all the environment and not explicit substitutions :

    a <- 1
    b <- 2
    c <- 3
    foo <- quote(a + a)
    bar <- quote(foo ^ b)
    zz <- quote(bar + c) 
    
    fun <- function(x){
    while(x != (
      x <- do.call(substitute, list(x, as.list(parent.frame())))
    )){}
      eval.parent(x)
    }
    fun(bar)
    # [1] 4
    fun(zz)
    # [1] 7
    fun(foo)
    # [1] 2
    

提交回复
热议问题