What's the difference between substitute and quote in R
Here's an example that may help you to easily see the difference between quote()
and substitute()
, in one of the settings (processing function arguments) where substitute()
is most commonly used:
f <- function(argX) {
list(quote(argX),
substitute(argX),
argX)
}
suppliedArgX <- 100
f(argX = suppliedArgX)
# [[1]]
# argX
#
# [[2]]
# suppliedArgX
#
# [[3]]
# [1] 100
How can one make visible the difference in the outputs of quote() and substitute()?
The reason is that the behavior of substitute()
is different based on where you call it, or more precisely, what you are calling it on.
Understanding what will happen requires a very careful parsing of the (subtle) documentation for substitute()
, specifically:
Substitution takes place by examining each component of the parse tree
as follows: If it is not a bound symbol in env, it is unchanged. If it
is a promise object, i.e., a formal argument to a function or
explicitly created using delayedAssign(), the expression slot of the
promise replaces the symbol. If it is an ordinary variable, its value
is substituted, unless env is .GlobalEnv in which case the symbol is
left unchanged.
So there are essentially three options.
In this case:
> df1 <- data.frame(a = 1, b = 2)
> identical(quote(df1),substitute(df1))
[1] TRUE
df1
is an "ordinary variable", but it is called in .GlobalEnv
, since env
argument defaults to the current evaluation environment. Hence we're in the very last case where the symbol, df1
, is left unchanged and so it identical to the result of quote(df1)
.
In the context of the function:
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
The important distinction is that now we're calling these functions on X
, not df1
. For most R users, this is a silly, trivial distinction, but when playing with subtle tools like substitute
it becomes important. X
is a formal argument of a function, so that implies we're in a different case of the documented behavior.
Specifically, it says that now "the expression slot of the promise replaces the symbol". We can see what this means if we debug()
the function and examine the objects in the context of the function environment:
> debugonce(is.identical)
> is.identical(X = df1)
debugging in: is.identical(X = df1)
debug at #1: {
out <- identical(quote(X), substitute(X))
out
}
Browse[2]>
debug at #2: out <- identical(quote(X), substitute(X))
Browse[2]> str(quote(X))
symbol X
Browse[2]> str(substitute(X))
symbol df1
Browse[2]> Q
Now we can see that what happened is precisely what the documentation said would happen (Ha! So obvious! ;) )
X
is a formal argument, or a promise, which according to R is not the same thing as df1
. For most people writing functions, they are effectively the same, but the internal implementation disagrees. X
is a promise object, and substitute
replaces the symbol X
with the one that it "points to", namely df1
. This is what the docs mean by the "expression slot of the promise"; that's what R sees when in the X = df1
part of the function call.
To round things out, try to guess what will happen in this case:
is.identical <- function(X){
out <- identical(quote(A), substitute(A))
out
}
is.identical(X = df1)
(Hint: now A
is not a "bound symbol in the environment".)
A final example illustrating more directly the final case in the docs with the confusing exception:
#Ordinary variable, but in .GlobalEnv
> a <- 2
> substitute(a)
a
#Ordinary variable, but NOT in .GlobalEnv
> e <- new.env()
> e$a <- 2
> substitute(a,env = e)
[1] 2
Why does substitute change noquote text to a string in R?
You're using the wrong function, use parse
instead of noquote
:
text<-'paste(italic(yes),"why not?")'
noquote_text <- parse(text=text)[[1]]
sub<- substitute(paste("Hi",noquote_text),env=list(noquote_text= noquote_text))
# paste("Hi", paste(italic(yes), "why not?"))
noquote
just applies a class
to an object of type character
, with a specific print
method not to show the quotes.
str(noquote("a"))
Class 'noquote' chr "a"
unclass(noquote("a"))
[1] "a"
Would you please elaborate on your answer?
In R you ought to be careful about the difference between what's in an object, and what is printed.
What noquote
does is :
- add
"noquote"
to the class attribute of the object - That's it
The code is :
function (obj)
{
if (!inherits(obj, "noquote"))
class(obj) <- c(attr(obj, "class"), "noquote")
obj
}
Then when you print it, the methods print.noquote
:
- Removes the class
"noquote"
from the object if it's there - calls
print
with the argumentquote = FALSE
- that's it
You can actually call print.noquote
on a string too :
print.noquote("a")
[1] a
It does print in a similar fashion as quote(a)
or substitute(a)
would but it's a totally different beast.
In the code you tried, you've been substituting a string instead of a call.
R substitute(), to substitute values in expression, is adding unnecessary quotes
Maybe I misunderstood what you are doing, but the following seems to work:
form <- 'condition ~ (1|subject) + v'
var <- 'a'
covar <- c('b', 'c')
Then combine with paste and turn to formula directly:
covar <- paste(var, paste(covar, collapse=" + "), sep=" + ")
form <- formula(paste(form, covar, sep=" + "))
Output:
condition ~ (1 | subject) + v + a + b + c
Difference between quote and expression in R
expression
returns its arguments as a vector of unevaluated expressions.quote
returns its argument as an unevaluated expression.
Try this:
(e1 <- quote(sin(x+y)))
# sin(x + y)
(e2 <- expression(sin(x+y)))
# expression(sin(x + y))
str(e1)
# language sin(x + y)
str(e2)
# expression(sin(x + y))
str(e2[[1]])
# language sin(x + y)
all.equal(e1, e2)
# [1] "Modes of target, current: call, expression" "target, current do not match when deparsed"
all.equal(e1, e2[[1]])
# [1] TRUE
Another example:
e2 = expression(sin(x+y), x+y)
e2
# expression(sin(x + y), x + y)
e2[1]
# expression(sin(x + y))
e2[2]
# expression(x + y)
R: Substitute variables bound in all parent environments
Building on @MikkoMarttila's answer, I think the following does what I requested
do_something <- function(todo) {
# A helper to substitute() in a pre-quoted expression
substitute_q <- function(expr, env) {
eval(substitute(substitute(x, env), list(x = expr)))
}
substitute_parents <- function(expr) {
expr <- substitute(expr)
# list all parent envs
envs <- list()
env <- environment()
while (!identical(env, globalenv())) {
envs <- c(envs, env)
env <- parent.env(env)
}
# substitute in all parent envs
for (e in envs) {
expr <- substitute_q(expr, e)
}
# previously did not include globalenv() and
# substitute() doesnt "substitute" there
e <- as.list(globalenv())
substitute_q(expr, e)
}
cat(
paste(
deparse(substitute_parents(todo)),
collapse = "\n"
)
)
}
This gives
nested_do <- function() {
var_2 <- "not_this"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
}
var_1 <- "hello"
var_2 <- "goodbye"
do_something({
print(var_1)
Sys.sleep(100)
print("world")
print(var_2)
})
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }
nested_do()
#> {
#> print("hello")
#> Sys.sleep(100)
#> print("world")
#> print("goodbye")
#> }
Change the body of a function without quote marks
Assuming that the desired inputs are
- the
modelstring
function and - the text of the replacement as a character string,
userinput
use parse
to convert the string to an expression and then add [[1]] on the end to convert that from an expression to a call object:
userinput <- "b.w[1]*X.w[i,1]^exp(b.w[2]*X.w[i,2])" # as in question
body(modelstring)[[2]][[4]][[2]][[3]] <- parse(text = userinput)[[1]]
modelstring
## function ()
## {
## for (h in (l1i1[j]):(l1i2[j])) {
## w[h] <- b.w[1] * X.w[i, 1]^exp(b.w[2] * X.w[i, 2])
## }
## }
Here is a simpler example that may be useful to play around with:
# inputs
f <- function() { 37 }
s <- "pi * pi"
body(f)[[2]] <- parse(text = s)[[1]]
f()
## [1] 9.869604
Related Topics
Adding Labels on Curves in Glmnet Plot in R
Create an Arrow with Gradient Color
Addsma Not Drawn on Graph When Called from Function
Connect R and Vertica Using Rodbc
How to Create a Bar and Line Plot with R Dygraphs
Converting to Date in a Character Column That Contains Two Date Formats
The Representation of an Empty Argument in a "Call"
Sum Specific Columns Among Rows
Read CSV with Two Headers into a Data.Frame
Extent of Boundary of Text in R Plot
Find Overlapping Regions and Extract Respective Value
Remove Duplicate Rows of a Matrix or Dataframe
Developing Shiny App as a Package and Deploying It to Shiny Server