Stephen Tucker
2010-Feb-17 01:31 UTC
[R] side-effects in functions that replace object values and attributes
Hello list, I encountered some surprising behavior in R and wanted to check to see if I was imagining things. Previously, I thought that replacement/setter operators in prefix notation (e.g., `[<-`(x,1,y) rather than x[1] <- y) did not produce side effects (i.e., left 'x' unchanged), but just realized that this is not the case anymore (or has it always been this way? - I don't have access to old R distributions at the moment, but using R 2.10.1 on Ubuntu and OS X)? I went through the NEWS file but did not see a change documented if there was one, but just going by my recollection... In any case, my understanding was that when modifying a value or attribute of an object, R reassigned the modified object to the original symbol. For instance, the help file for `name<-`() says that> names(z)[3] <- "c2"is evaluated as> z <- "names<-"(z, "[<-"(names(z), 3, "c2"))But the final (re)assignment (`<-`) seems redundant as> invisible("names<-"(z, "[<-"(names(z), 3, "c2")))does the same thing. In this case, I wonder if there is a preferred way to use the replacement/setter operators in prefix notation without producing such side effects (replace() exists for `[<-`() and `[[<-`(), but perhaps something more general). For instance, if I did not desire the following modification in x:> x <- c("1","2") > y <- `names<-`(x,c("a","b")) > ya b "1" "2"> xa b "1" "2">I might take advantage of R's lazy evaluation and use create a copy in the local function environment and allow that copy to be modified:> x <- c("1","2") > y <- `names<-`(`<-`(x,x),c("a","b")) > ya b "1" "2"> x[1] "1" "2" The interesting thing is that `mode<-`(), while also a "setter" function in that it sets an object attribute, does not behave as `names<-`():> x <- c("1","2") > y <- `mode<-`(x,"integer") > y[1] 1 2> x[1] "1" "2"> mode(x)[1] "character" So another question that naturally arises is whether there a general rule by which we can predict the behavior of these types of operators (whether they produce side effects or not)? Thanks, Stephen
William Dunlap
2010-Feb-17 01:50 UTC
[R] side-effects in functions that replace object values andattributes
> -----Original Message----- > From: r-help-bounces at r-project.org > [mailto:r-help-bounces at r-project.org] On Behalf Of Stephen Tucker > Sent: Tuesday, February 16, 2010 5:32 PM > To: r-help at r-project.org > Subject: [R] side-effects in functions that replace object > values andattributes > > Hello list, > > I encountered some surprising behavior in R and wanted to > check to see if I was imagining things. Previously, I thought > that replacement/setter operators in prefix notation (e.g., > `[<-`(x,1,y) rather than x[1] <- y) did not produce side > effects (i.e., left 'x' unchanged), but just realized that > this is not the case anymore (or has it always been this way? > - I don't have access to old R distributions at the moment, > but using R 2.10.1 on Ubuntu and OS X)? I went through the > NEWS file but did not see a change documented if there was > one, but just going by my recollection... > > In any case, my understanding was that when modifying a value > or attribute of an object, R reassigned the modified object > to the original symbol. For instance, the help file for > `name<-`() says that > > > names(z)[3] <- "c2" > > is evaluated as > > > z <- "names<-"(z, "[<-"(names(z), 3, "c2")) > > But the final (re)assignment (`<-`) seems redundant as > > > invisible("names<-"(z, "[<-"(names(z), 3, "c2"))) > > does the same thing. > > In this case, I wonder if there is a preferred way to use the > replacement/setter operators in prefix notation without > producing such side effectsI would never recommend using replacement operators this way. In part this is because S+ doesn't like it: S+> x<-1:10 S+> `[<-`(x, 1, value=66.6) [1] 66.6 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 Warning messages: looks like the internal reference count was not updated in: x[1] <- 66.6 S+> x [1] 1 2 3 4 5 6 7 8 9 10 in part because it is ugly, but mostly it is because forcing the program to accept such usage closes off, or at least restricts, an avenue for saving memory when doing replacement operations. If you have a replacement function that you are tempted to use in this way, I recommend that you write a wrapper function for it. E.g., instead of using namedX <- `names<-`(x, value=as.character(x)) write setNames <- function(x, names) { names(x) <- names x } and use it as namesX <- setNames(x, as.character(x)) Bill Dunlap Spotfire, TIBCO Software wdunlap tibco.com> (replace() exists for `[<-`() and > `[[<-`(), but perhaps something more general). For instance, > if I did not desire the following modification in x: > > > x <- c("1","2") > > y <- `names<-`(x,c("a","b")) > > y > a b > "1" "2" > > x > a b > "1" "2" > > > > I might take advantage of R's lazy evaluation and use create > a copy in the local function environment and allow that copy > to be modified: > > > x <- c("1","2") > > y <- `names<-`(`<-`(x,x),c("a","b")) > > y > a b > "1" "2" > > x > [1] "1" "2" > > The interesting thing is that `mode<-`(), while also a > "setter" function in that it sets an object attribute, does > not behave as `names<-`(): > > > x <- c("1","2") > > y <- `mode<-`(x,"integer") > > y > [1] 1 2 > > x > [1] "1" "2" > > mode(x) > [1] "character" > > So another question that naturally arises is whether there a > general rule by which we can predict the behavior of these > types of operators (whether they produce side effects or not)? > > Thanks, > > Stephen > > ______________________________________________ > R-help at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide > http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >