Using get() with replacement functions

Can anyone explain to me why the following example occurs?

#Create simple dataframe
assign( "df" , data.frame( P = runif(5) , Q = runif(5) , R = runif(5) ) ) 

#Return the dataframe from the given character vector
get( "df" ) 
            P          Q          R
1  0.17396222 0.90994676 0.90590685
2  0.33860092 0.98078739 0.38058921
3  0.80751402 0.93229290 0.82853094
4  0.05460417 0.55448507 0.01605027
5  0.04250316 0.03808318 0.40678270

#Return the column names of df
colnames( get( "df" ) )
[1] "P" "Q" "R"

#But using a replacement function...
colnames( get( "df" ) ) <- c( "S" , "T" , "U" ) 
    Error in colnames(get("df")) <- c("S", "T", "U") : 
    target of assignment expands to non-language object

I'd A) like to know why the replacement functions won't work in this way with get()?

And b) if there is some way to work around this, given my problem which I outline below;

My problem is that I have many objects, created (using a toy example) in a loop, something like this: assign( paste( "Object" , i , sep = "." ) , rnorm(1000 , i) ), where i is a vector, say i <- 1:1000 and then I would like to be able to assign names (for instance from a different vector) to each object in the loop, but names( get( paste( "Object" , i , sep = "." ) ) <- someNewName doesn't work as in the example above.

But get( paste( "Object" , i , sep = "." ) ) does return the names (or NULL) of those objects.

Thanks!


To understand why this doesn't work, you need to understand what colnames<- does. Like every function in that looks like it's modifying an object, it's actually modifying a copy, so conceptually colnames(x) <- y gets expanded to:

copy <- x
colnames(copy) <- y
x <- copy

which can be written a little more compactly if you call the replacement operator in the usual way:

x <- `colnames<-`(x, y)

So your example becomes

get("x") <- `colnames<-`(get("x"), y)

The right side is valid R, but the command as a whole is not, because you can't assign something to the result of a function:

x <- 1
get("x") <- 2
# Error in get("x") <- 2 : 
#  target of assignment expands to non-language object

Using assign in the way you demonstrate in the question is at least uncommon in R. Normally you would just put all objects in a list.

So, instead of

for (i in 1:10) {
assign( paste( "Object" , i , sep = "." ) , rnorm(1000 , i) )}

you would do

objects <- list()
for (i in 1:10) {
objects[[i]] <- rnorm(1000 , i) }

In fact, this construct is so common that there is a (optimized) function (lapply), which does something similar:

objects <- lapply(1:10, function(x) rnorm(1000,x))

You can then access, e.g., the first object as objects[[1]] and there are several functions for working with lists.