Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In R, exactly what causes an object of type name (or symbol) to be evaluated?

After running:

x <- as.name("aa")
aa <- 2

in R, why doesn't

(x)

return 2? And why doesn't

x <- as.name("aa")
aa <- 3
get(get(x))

return 3?

I know get() expects a string, but I don't understand why it doesn't evaluate x, find the string inside, and then get that. It seems to me like sometimes functions do such evaluation of their arguments, and sometimes they don't. For instance, in the second example, if you replace get(get(x)) with eval(x), eval() evaluates the x to find the name, and then evaluates the name to find 3.

like image 253
andrewH Avatar asked May 06 '16 00:05

andrewH


2 Answers

Because the value of x is not 2, it is the symbol (or name) aa. However, if you eval it:

> eval(x)
[1] 2

Similarly, get(x) doesn't work at all (i.e. produces an error) because as per the documentation for get, it's first argument must be an object name (given as a character string), where the parenthetical is meant to distinguish it from a symbol/name.

get only works with a character argument:

 > get("aa")
[1] 2

And a symbol (which I find less confusing than name) is not the same thing:

> identical("aa",as.name("aa"))
[1] FALSE

(as.name and as.symbol do the same thing.)

For an excellent explanation of the "evaluation of expressions" vs "evaluation of function arguments" distinction I mention below in a comment, see @MrFlick's answer.

like image 132
joran Avatar answered Nov 12 '22 22:11

joran


I think @joran's answer is right but maybe I can try to explain a different way.

The ( "function" in R is essentially the identity function. It echoes back what you pass it. It's almost like it isn't there. There is no difference between what will be returned by these statements

x      #1
(x)    #2
((x))  #3

The parenthesis just passthrough the value inside. You can add as many parenthesis as you want and it will not change what's returned. The evaluator looks at ((x)), see the outer parenthesis, and knows to just return the value of the thing inside the parenthesis. So now it's parsing just (x), and again, it sees the outer parenthesis and will just return the value inside the parenthesis, which is x. The parenthesis just pass though the value from the inside; they do not evaluate it.

The bare value x is a name (or symbol). A name is not uniquely tied to a value. The mapping between names and values differs by environment. That's why names must be evaluated in a particular context to get a value. Consider these examples

aa <- 5
dd <- data.frame(aa=20)
x <- as.name("aa")
foo <- function(x) {aa<-10; eval(x)}

eval(x)
# [1] 5
foo(x)
# [1] 10
eval(x, dd)
# [1] 20

This behavior is actually highly desirable. It's what makes features that require non-standard evaluation work, like

subset(mtcars, hp<100)

When you are using the R console, it behaves as a REPL -- it reads your input, evaluates it, prints it, and then waits for the next input. Note that it only does one level of evaluation and the evaluation happens in the "current" environment. It does not recursively evaluate the returned value from an expression. So when you do

x <- as.name("aa")
x   # identical to (x)
# aa

when the REPL gets to the evaluation step, it evaluates the name x which points to the name aa. That's it. One level of evaluation. The name aa is not subsequently evaluated.

There is a note in the ?eval help page that says this:

eval evaluates its first argument in the current scope before passing it to the evaluator

There's not a "double" evaluation happening there. It is merely evaluating it's parameters just as any other function in R does. For examples

aa <- 5 
bar <- function(x) print(x)
bar(aa+2)
# [1] 7

It prints "7", not "aa+2" because the function has evaluated it's parameter prior to printing. It also explains the differences between these two

dd <- data.frame(bb=20)
xx <- as.name("bb")
eval(bb, dd)
# Error in eval(bb, dd) : object 'bb' not found
eval(xx, dd)
# [1] 20

In the first eval() call, R is unable to evaluate bb in the current environment so you get the error. But note that

evalq(bb, dd)

does work because evalq does not try to evaluate the first expression parameter.

like image 5
MrFlick Avatar answered Nov 12 '22 22:11

MrFlick