7

disclaimer: this code is bad practice., and only works due to something bug-like. Never use it in a real situation. This question is about the interesting behaviour of R, nothing else than that.

After reading this question I got pretty puzzled. Apparently, ifelse can access information that should be hidden.

Say we do :

> x <- expression(dd <- 1:3)    
> y <- expression(dd <- 4:6)    
> z <- c(1,0)

> eval(x)
> eval(y)
>

We get no output. Logic, as both expressions are actually assignments of a vector dd. eval() is not supposed to give output then. But strangely enough, when you try the funny code

> ifelse(z==0,eval(x),eval(y))
[1] 4 2

You get output??? Somebody has an explanation for this?

It's not as simple as "R evaluates and then uses dd". Whatever order you give z, whatever condition you use, dd is always the last mentioned eval().

> ifelse(z==0,eval(x),eval(y))
> dd
[1] 4 5 6

> ifelse(z==1,eval(x),eval(y))
> dd
[1] 4 5 6

> z <- c(0,1)
> ifelse(z==0,eval(x),eval(y))
> dd
[1] 4 5 6

> ifelse(z==1,eval(x),eval(y))
> dd
[1] 4 5 6

> ifelse(z==1,eval(y),eval(x))
> dd
[1] 1 2 3

EDIT:

a closer look at the source code of ifelse shows that the line making sure this happens, is the rep() :

> x <- expression(dd <- 1:3)
> eval(x)
> rep(eval(x),2)
[1] 1 2 3 1 2 3

Still, it doesn't solve the question...

Community
  • 1
  • 1
Joris Meys
  • 106,551
  • 31
  • 221
  • 263

2 Answers2

5

This is not a bug

The 'output' onto the console of the result of a command is conditional. This can be determined by the function itself - for example:

> f=function(x)x;
> g=function(x)invisible(x);
> f(1)
[1] 1
> g(2)
> .Last.value
[1] 2

The value is still being returned just fine - it's just not printed on the console.

What's happening here is the eval marks its output invisible but rep and ifelse do not, and in fact effectively strip the invisible property off their input.

It appears the invisible is a special property of the variable, and is not passed through the rep operation. It's also not passed through assignment:

> h=function(x){y=x;y;}
> f(g(1))
> h(g(1))
[1] 1
>

See ?invisible for a little more background.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
2

R always evaluates the two alternatives to an ifelse command. You can rationalize that as being necessary in order to be ready to choose which item in each vector to return to the calling environment. The opposite is true of an if (cond) {affirm-conseq} else {neg-conseq}. The basis of "dd" always getting set based on evaluation of the third ifelse argument is revealed when on looks at the code for ifelse. The "no"-vector code gets evaluated after the "yes"-vector in order to choose which items in negative consequent vector get assigned to the "ans"-output vector.

IRTFM
  • 258,963
  • 21
  • 364
  • 487