1

Referring to this example:

z <- list(a = 1, b = "c", c = 1:3)  
names(z)  
# change just the name of the third element.  
names(z)[3] <- "c2"  
z  

The names documentation says "see the examples. This works because the expression there is evaluated as z <- "names<-"(z, "[<-"(names(z), 3, "c2"))."

What is this final line of code about? It matches no R syntax that I'm familiar with, so I can't even fathom how to read it. If anything, it seems more complex than the example that it's trying to explain. How do I read it?

J. Mini
  • 1,868
  • 1
  • 9
  • 38
  • `names<-` is a different function which does the assignment – akrun Dec 20 '20 at 22:13
  • @akrun I understood that much, but `"names<-"` was a step too far for me. – J. Mini Dec 20 '20 at 22:16
  • If you type on the console. you will get ``names<-` function (x, value) .Primitive("names<-")# names# function (x) .Primitive("names")` – akrun Dec 20 '20 at 22:17
  • There are some functions that have similar kind of assignment counterparts e.g. `substring` – akrun Dec 20 '20 at 22:18
  • 2
    Just remember that often when you have `funcname(leftside) <- rightside` that is actually calling `\`funcname<-\`(leftside, rightside)` – thelatemail Dec 20 '20 at 22:21
  • 1
    I used backquotes instead of double quotes as double quotes can be evaluated as string, while backquotes evaluates as is – akrun Dec 20 '20 at 23:10
  • @thelatemail I've found otherwise https://stackoverflow.com/q/65395373/10319707 – J. Mini Dec 21 '20 at 15:22

1 Answers1

3

That part of the documentation is indeed badly written, I don't blame you for being confused.

Here is the short answer, say you have a vector z = c(a = 1, b = 2, c = 3), then the following are equivalent:

names(z)[3] <- "c2"

and

z <- "names<-"(z, "[<-"(names(z), 3, "c2"))

That's all the documentation is saying.

Here's the longer answer: in R you are probably familiar with the basic syntax of a function, so let's say you have foo = function(x,y) x + y then you are probably used to calling foo(1, 2). You may also have seen 'infix' operators such as +, so you're probably familiar with 1 + 2. However internally this is actually read as "+"(1,2) (try this it will work). A similar thing is seen with assignment and extraction functions, like x[1] (for some vector x) or as in this example names(z)[3] <- where <- is the assignment operator.

To make this example even more complicated there's actually two separate infix operators being used! There's names<- and there's [<-, the first one is used to set the name of a vector and the second one is used to set a value in a vector after having first extracted the vector (I know there's a lot going on here).

So breaking down what that line is telling in more familiar R code:

z <- "names<-"(z, "[<-"(names(z), 3, "c2"))
  1. z: Original vector
  2. y = names(z): Character vector - names of z.
  3. y[3]<-"c2": Extract the third element of y and assign it value "c2". Now we are at "[<-"(names(z), 3, "c2").
  4. names(z)<-y: Set the names of z as y. Now we are at "names<-"(z, "[<-"(names(z), 3, "c2")).

Just to make things even more confusing "names<-" doesn't save the newly named vector, it just sets the new names. Which is why z <- "names<-"... is required at the end to overwrite the original variable with the newly named one.

And showing this with R code:

> z = c(a = 1, b = 2, c = 3)
> print(z)
a b c 
1 2 3 
> names = names(z)
> names[3] = "c2"
> names(z) = names
> print(z)
 a  b c2 
 1  2  3 
RaphaelS
  • 839
  • 4
  • 14
  • If `"[<-"` is assignment and extraction, then why doesn't `"[<-"(names(z), 3, "c2")` mean "take the third element of names(z) and assign to it "c2"?" – J. Mini Dec 20 '20 at 22:42
  • Using variables as examples: `x[y] <- z` is equivalent to `"[<-"(x,y,z)`. First argument is the vector to assign to, the second is the index of the element to change, third is the replacement value. So here we are saying: from the names of `z` replace the third value with "c2". Test this with: `'[<-'(letters, 1, "d")` – RaphaelS Dec 20 '20 at 22:56
  • I think you've missed a step 5: the last `z <-` bit. It's probably also worth pointing out that `"[<-"(names(z), 3, "c2")` returns **all** of z's names. – J. Mini Dec 20 '20 at 23:06
  • I've improved the example (I hope) – RaphaelS Dec 20 '20 at 23:13
  • So the thing to know about `names(z)<-` is that it doesn't actually just take a vector and change its names, what it does is: i) get the names; ii) change the names; iii) overwrite the original variable with the new names. This is a very subtle point in R coding and the only way to really understand it is to test it, just run the following and you'll see what I mean: `z = c(a = 1, b = 2, c = 3); "names<-"(z, "[<-"(names(z), 3, "c2"))`. You should be able to see that `"names<-"` doesn't actually save the result, so you have to overwrite `z` – RaphaelS Dec 21 '20 at 09:00
  • I agree, I edited the answer after I wrote the comment. Happy your question is now answered. And also thanks for the challenge of explaining it. – RaphaelS Dec 21 '20 at 13:25
  • I'm still not entirely comfortable with this. The suggestion that `"names<-"(x,y)` is not identical to `names(x)<-y` seems to run contrary to how inflix operators are supposed to work. – J. Mini Dec 21 '20 at 14:48
  • It is strange, I guess I haven't thought about it before. This is just true of all assignment operators in R, for example: `x = 1:2; "[<-"(x,1,3)`. I guess this is to do with copy-on-modify semantics? – RaphaelS Dec 21 '20 at 15:04
  • 1
    I'll ask a new question about it. – J. Mini Dec 21 '20 at 15:11