splice in a bquote in R
Asked Answered
N

3

11

Say that I'm building up an expression with R's backquote operator bquote, and I'd like to "splice" in a list at a specific position (that is, lose the outer parenthesis of the list).

For example, I have the expression "5+4", and I'd like to prepend a "6-" to the beginning of it, without using string operations (that is, while operating entirely on the symbol structures).

So:

>   b = quote(5+4)
>   b
5 + 4
>   c = bquote(6-.(b))
>   c
6 - (5 + 4)
>   eval(c)
[1] -3

I would like that to return the evaluation of "6-5+4", so 5.

In common lisp, the backquote "`" operator comes with a splice operator ",@", to do exactly this:

CL-USER> 
(setf b `(5 + 4))
(5 + 4)
CL-USER> 
(setf c `(6 - ,@b))
(6 - 5 + 4)
CL-USER> 
(setf c-non-spliced `(6 - ,b))
(6 - (5 + 4))
CL-USER> 

I tried using .@(b) in R, but that didn't work. Any other ideas? And to restate, I do not want to resort to string manipulation.

Noellenoellyn answered 20/2, 2014 at 0:23 Comment(1)
I think at a fundamental level, it is all "string manipulation".Audie
N
6

This is an interesting question. After playing with things a bit, this is all I could come up with for your particular example.

> b <- quote(5 + 4)
> b[[2]] <-  bquote(6 - .(b[[2]]))
> b
6 - 5 + 4
> eval(b)
[1] 5

Unfortunately, this may be hard to generalize, given the fact that you have to take into account the order of evaluation, etc.

Nap answered 20/2, 2014 at 2:26 Comment(0)
A
3

You need to realize that R expressions are not stored with the infix order that their print method may lead you to believe:

> b = quote(5+4)
>  b[[1]]
`+`
> b = quote(5+4)
>  b[[2]]
[1] 5
> b = quote(5+4)
>  b[[3]]
[1] 4

Whereas the print method for language objects might make you think the the second argument to 6 -5 +4 is 6-5,....

> b2 <- bquote(6 - 5 + 4)
> b2[[1]]
`+`
> b2[[2]]
6 - 5

.... that is also an illusion. It really has a list structure. (I would have thought this would be expected by a Lisp user.)

> b2[[3]]
[1] 4
> b2[[2]][[1]]
`-`
> b2[[2]][[2]]
[1] 6
> b2[[2]][[3]]
[1] 5
Audie answered 20/2, 2014 at 1:9 Comment(6)
I do realize that it is an AST under the hood, but that doesn't really directly solve the problem.Noellenoellyn
I agree it doesn't solve the problem, but I thought defining the proper dimensions of the problem would be helpful. I cannot quite believe that this was the real problem anyway so I rather hoping to see what the "use case" was.Audie
This definitely hints towards a way to implement .@. I'm sort of hoping there's a code-walking library that already does it that someone knows about.Noellenoellyn
Not sure where to find or even what is a 'code-walking library', but have you looked at the code for bquote?Audie
@ClaytonStanley there's a walkCode function in the codetools package... I don't know if it helps.Loanloanda
@IShouldBuyABoat If bquote had splice capability, here is a potential use case that was just posted on SO: https://mcmap.net/q/1018702/-apply-different-functions-to-different-sets-of-columns-by-groupNoellenoellyn
S
2

In R devel version to be released as R 4.0.0 there is a new feature for splice expression in bquote. Related news entry from https://cran.r-project.org/doc/manuals/r-devel/NEWS.html

The backquote function bquote() has a new argument splice to enable splicing a computed list of values into an expression, like ,@ in LISP's backquote.

And manual example from https://stat.ethz.ch/R-manual/R-devel/library/base/html/bquote.html

exprs <- expression(x <- 1, y <- 2, x + y)
bquote(function() {..(exprs)}, splice = TRUE)
#function() {
#    x <- 1
#    y <- 2
#    x + y
#}

Unfortunately it does not address your use case:

e = quote(5 + 4)
bquote(6 - ..(e), splice=TRUE)
#6 - (5 + 4)

Dropping the parenthesis is not easily possible because it would need to rotate AST. It is caused by the way how R implements AST.

Please follow the other answers for a workable solution.

Subulate answered 15/3, 2020 at 3:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.