5

I am trying to understand the R expression object, but encountered some difficulties.

Code snippet:

a = 1
b = 2
x = expression(sin(a+b+1))
print(class(x[[1]][2]))
eval(x[[1]][2])

Results:

#////////////////////////////////////////////////////
x = expression(sin(a+b+1))
#////////////////////////////////////////////////////
print(class(x[[1]][2]))
[1] "call"
#////////////////////////////////////////////////////
x = expression(sin(a+b+1))
#////////////////////////////////////////////////////
print(class(x[[1]][2]))
[1] "call"
#////////////////////////////////////////////////////
eval(x[[1]][2])
Error in eval(expr, envir, enclos) : attempt to apply non-function
2: eval(expr, envir, enclos)
1: eval(x[[1]][2])

x[[1]][2] is a call object, but why can't it be evaluated?

gagolews
  • 12,836
  • 2
  • 50
  • 75
qed
  • 22,298
  • 21
  • 125
  • 196

1 Answers1

11

You should use the [[ operator, and not the [.

a <- 1
b <- 2
eval(x[[1]][[2]])
## [1] 4

This is because you'd like to extract information from the language object, and not to subset it (look inside the 2nd element, and not return a subsequence consisting of the 2nd element).

In other words, subsetting a call gives you a call:

x[[1]][2]
## (a + b + 1)()

and because there is no such function as a+b+1 (in fact, the result of a+b+1's evaluation is not a function object), R throws an error.

Interestingly, if + would return a function object, this could make sense:

"+" <- function(a, b) { function() print(":-)") }
(a+b+1)()
[1] ":-)"

On the other hand, extracting an element from a call object gives you an expression that can be evaluated:

x[[1]][[2]]
a + b + 1

(BTW, this expression is also a call, here equivalent to "+"(a, "+"(b, 1)).

EDIT. More formally, a call is an expression (sequence) of the form:

(f, a1, ..., an),

which we normally read as:

f(a1, ..., an).

Thus, the first element of the sequence is an object used to transform the other elements to get an output value.

Here x[[1]] is equivalent to:

(sin, a+b+1)

or, in more detail,

(sin, (+, a, (+, b, 1))).

Thus, x[[1]][2] takes a subsequence of the above consisting of only the 2nd element and returns:

((+, a, (+, b, 1)))

(i.e. (a+b+1)() - no args!). On the other hand x[[1]][[2]] extracts (looks inside) the 2nd element and gives:

(+, a, (+, b, 1)),

i.e. a+b+1 (note one pair of parentheses less).

EDIT2: All of this exemplifies the beauty and expressiveness of the R language, at least IMHO. Let's study another example in which we create a call f1(f2(f3, f4), f3, f4), which may be represented by a sequence

(f1, (f2, f3, f4), f3, f4).

We have:

f <- function(...) invisible(NULL)
f1 <- f; f2 <- f; f3 <- f; f4 <- f # for sake of clarity below
expr <- quote(f1(f2(f3, f4), f3, f4))
print(expr)
## f1(f2(f3, f4), f3, f4),           i.e. (f1, (f2, f3, f4), f3, f4)
print(expr[1:3])
## f1(f2(f3, f4), f3),               i.e. (f1, (f2, f3, f4), f3)
print(expr[3:4])
## f3(f4),                           i.e. (f3, f4)
print(expr[3])
## f3(),                             i.e. (f3)
expr[2]
## f2(f3, f4)(),                     i.e. ((f2, f3, f4)) [subsetting!]

And now for something completely different:

expr[[2]]
## f2(f3, f4),                       i.e. (f2, f3, f4) [extraction]

Hope this clarifies these issues a little bit.

gagolews
  • 12,836
  • 2
  • 50
  • 75
  • Both class(x[[1]][[2]]) and class(x[[1]][2]) are calls. – qed Apr 23 '14 at 15:28
  • 3
    One gives you a call to `(a+b+1)()` and the other one to `"+"(a, "+"(b, 1))`. – gagolews Apr 23 '14 at 15:30
  • 1
    I've made some edits, hope it clarifies more of this complicated topic. :) – gagolews Apr 23 '14 at 15:32
  • And more edits (BTW, [this book](http://rksiazka.rexamine.com/) explains this topic in very detail, unfortunately it's in Polish...) – gagolews Apr 23 '14 at 15:40
  • 2
    Oh, I would like to give you 10 upvotes! Is the book freely available? – qed Apr 23 '14 at 15:41
  • Unfortunately, the publisher does not allow its sharing. :( – gagolews Apr 23 '14 at 15:43
  • 1
    I have a Polish colleague in the office! – qed Apr 23 '14 at 15:43
  • Is there a similar book in English? – qed Apr 23 '14 at 15:46
  • Try with Chamber's books, see http://stackoverflow.com/questions/1744861/how-to-learn-r-as-a-programming-language – gagolews Apr 23 '14 at 15:48
  • Another indicator of the difference: ≤n>h(x[[1]][2])length(x[[1]][2]) is 11 while ≤n>h(x[[1]][[2]])length(x[[1]][[2]]) is 33 . Annoyingly enough, tho' both `eval(x[1])` and `eval(x[[1]])` yield the desired calculation. – Carl Witthoft Apr 23 '14 at 17:51
  • This also makes sense for me: you can evaluate an expression (containing for example multiple calls == a sequence of calls) like `x[1]` or a single call like `x[[1]]`... – gagolews Apr 23 '14 at 18:12