Reputation: 4534
When defining a default argument y
to an R function as being equal to an other argument x
. Ex: function(x,y=x)
. I found that if I change the other argument (x
) before using my default argument (y
), it also changes the value of y.
I understand it has to do with "delayed copying until modified" but is that really the desired behavior?
Hereafter a full example:
oh_my <- function(x, y=x){
x <- rev(x)
y <- rev(y) # the value of y here actually is rev of initial x!
print(y)
}
oh_my(1:5)
[1] 1 2 3 4 5
An easy solution would be:
ok <- function(x, y=NULL){
if(is.null(y)){
y<-x
}
x <- rev(x)
y <- rev(y)
print(y)
}
ok(1:5)
[1] 5 4 3 2 1
But I actually like the fact that the default is obvious in the first option (including in automatically generated help files).
An other solution would be:
pfiouu <- function(x, y=x){
y <- rev(y) # the value of y here actually is rev of initial x!
x <- rev(x)
print(y)
}
pfiouu(1:5)
[1] 5 4 3 2 1
But it really seems awkward to me that pfiouu
and oh_my
give different results as the two exchanged lines do not mention explicitly each others variables and yet yield different results!
Am I missing a good practice that would keep the default obvious and avoid that kind of fall-trap?
Upvotes: 1
Views: 546
Reputation: 4534
As suggested by MrFlick, adding force(y)
at the start of the function allows to force the evaluation of y
and prevents issues like that. It being a desirable behavior in such a high level programming language as R remains open though.
no_surprise<- function(x, y=x){
force(y)
x <- rev(x)
y <- rev(y)
print(y)
}
no_surprise(1:5)
[1] 5 4 3 2 1
As expected.
Upvotes: 1