R uses three types of language objects for modifications; expressions, calls, and functions. However, in this ebook you will learn about call objects. Call objects are also called “unevaluated expressions”.
The best way to obtain a call object is by using the quote() function with an expression argument.
The following examples use the quote() function with expression arguments:
> obj1 <- quote(2 + 2)
> obj2 <- quote(plot(x, y))
The arguments are not evaluated, but the results are parsed arguments. In the above example, obj1 and obj2 can also be evaluated with using eval or just be manipulated as data. Obviously, obj2 uses “call”
because a call is made to the plot function with the x and y arguments. The “obj1” object also uses the same structure as a call to the binary operator along with two arguments.
This following example explains the structure:
> quote("+"(2, 2)) 2+2
The contents of the call object are accessed with a list type syntax, which can be convert to and from lists with the “as.list” and “as.call”. When the keyword argument matching is used in the following ways, the keywords can be used as list tags:
> obj3 <- quote(plot(x = age, y = weight))
> obj3$x age
> obj3$y weight
The contents of the call object have the “name” mode, based on the previous example. The identifiers in the calls are true, but the contents within the call are constants. Constants can be of any type, but the first content must be a function if the call is successfully evaluated.
Objects with the mode name maybe created from character strings using as.name.
This is how you would modify the obj2 using as.name:
obj2[[1]] <- as.name("+") > obj2x+y
The following example shows that subexpressions are actually calls:
> obj1[[2]] <- obj2 > obj1x+y+2
The grouped parentheses inputted are saved in parsed expressions. They represent functions with one argument, therefore 4 – (2-2) results in "-"(4, "(" ("-"(2, 2))) in prefix notation. The open brace (‘(‘) operator in this case returns an argument. This can be an issue, but it can be challenging to write a parser/deparser that saves the user input, store it minimally, and ensure that parsing the deparsed expression returns the same expression.
Noticeably, the parser in R is not really invertible or the deparser as the following examples will show:
> str(quote(c(1,2))) language c(1, 2)
> str(c(1,2)) val [1:2] 1 2
> deparse(quote(c(1,2))) [1] "c(1, 2)"
> deparse(c(1,2)) [1] "c(1, 2)"
> quote("-"(2, 2)) 2-2
> quote(2 - 2) 2-2
Deparsed expressions should evaluate to equal values to the original expression.
The internal part of an expression does not need modifications regularly. However, the user may want to get an expression to deparse and use it for labeling plots.
Here is example of how this can be done:
> xlabel <- if (!missing(x))
> deparse(substitute(x))
In the above example the variable or expression with the x argument will be used for labeling the x axis. The substitute() function is used to achieve this. It takes the x expression and substitutes the expression that was passed previously with the x argument. This will happen when x carries the information about the expression creating the value.
A formal argument intends for an object to have the following three slots:
1. A slot for the expression that defines the object.
2. A slot for the environment to evaluate the expression.
3. A slot for the value for the expression that was already evaluated.
If a substitute was invoked within the function, the local variable will also be open to substitution. The substitution argument does not necessarily have to be a simple identifier. Instead it can be an expression that involves several variables and substitutions. The substitute function also has an additional argument that can be an environment or a list. The following example shows how this works.
> substitute(x + y, list(x = 1, y = quote(a))) 1+a
In the above example, quoting is used to substitute x. This example works well in circumstances where math expressions are required to create graphs, as shown in the following example:
> plot(0)
> for (i in 1:4) + text(1, 0.2 * i,
+ substitute(x[ix] == y, list(ix = i, y = pnorm(i))))
The substitutions are purely verbiage and there is no testing to see if the call objects make sense when they are evaluated. The substitute(x <- x + 1, list(x=2)) will return 2 <- 2 + 1. It is important to note that R sets its own rules and execute expressions based on what makes sense. If an expression does not make sense, R might still some use for them. Using some mathematical expressions in graphs for example may involve some constructions that are constructed correctly, however can be meaningless when it is evaluated, for example, “{} >= 20 * “years”’.
Substitute does not evaluate the first argument, which can be confusing when you are substituting an object within a variable. To resolve this issue, you should use the substitute() function more than once.
The following example shows how to use the substitute() function more than once:
> expr <- quote(x + y)
> substitute(substitute(y, list(x = 2)), list(y = expr))
> substitute(x + y, list(x = 2))
> eval(substitute(substitute(y, list(x = 2)), list(y = expr))) 2+z
R provides the following rules for substitutions:
In each symbol of the parse tree, the first one corresponds with second. It can be a tagged list or an environment frame.
A simplified local object inserts a value; otherwise it corresponds to the global environment.
The expression of a potential function argument is substituted.
When the symbol does not correspond, it is not used.
The special exception at a higher level is different because it was inherited from the S language. Therefore, there no control over which variables would be bound at this level and it is better to make substitute behave like a quote.
The rule relating to the potential substitution is a little different from the one used in the S language when the local variable is modified before the substitution. In this case, R will use the new value of the variable. S however will unconditionally use the argument within the expression (unless it was used as constant). This means that f(1) in R maybe completely different from f(1) used in S. The usage in R is cleaner.
Consider the following code to get a better understanding of the substitution:
plotlog <- function(x, xlab = deparse(substitute(x))) { x <- log(x)
plot(x, xlab = xlab) }
This may seem clear, but actually the x label becomes a poor expression. This occurs because the
“lazy evaluation” rule causes the “xlab” expression to happen after x is modified. The solution is to ensure the “xlab” expression is evaluated first.
The following example shows how to evaluate the “xlab” expression first:
plotlog <- function(x, xlab = deparse(substitute(x))) { xlab
x <- log(x)
plot(x, xlab = xlab) }
You will notice in the above example that eval(xlab) is not used. If xlab is a language or an expression object, then the object will be evaluated as well.
The R variant for the substitute() is the bquote() function. It is used to replace some subexpressions with values. The following examples show how to replace the substitute() function with the bquote() function. The syntax for the bquote() function is actually borrowed from the List Processing (LISP) program back quote macro.
// The substitute() function is used here!
> plot(0)
> for (i in 1:4)
> text(1, 0.2 * i, substitute(x[ix] == y, list(ix = i, y = pnorm(i))))
// The bquote() function is used instead of the substitute() function.
> plot(0)
> for(i in 1:4)
> text(1, 0.2*i, bquote( x[.(i)] == .(pnorm(i)) ))
In the above example, the expression used in the bquote() function is quoted except the contents within the subexpressions, which are replaced with its values.