Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't this R call object in an expression be evaluated? (subsetting vs extracting from a call object)

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?

like image 913
qed Avatar asked Apr 23 '14 15:04

qed


1 Answers

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.

like image 112
gagolews Avatar answered Nov 01 '22 11:11

gagolews