Another R tip. Use seq_len()
to avoid the backwards sequence trap.
Many R users use the “colon sequence” notation to build sequences. For example:
for(i in 1:5) { print(paste(i, i*i)) } #> [1] "1 1" #> [1] "2 4" #> [1] "3 9" #> [1] "4 16" #> [1] "5 25"
However, the colon notation can be unsafe as it does not properly handle the empty sequence case:
n <- 0 1:n #> [1] 1 0
Notice the above example built a reversed sequence, instead of an empty sequence.
This leads to the backwards sequence trap: writing code of the form “1:length(x)
” is often wrong. For example “for(i in 1:length(x)) { statements involving x[[i]] }
“, which will fail for length-zero x
.
To avoid this use seq_len()
or seq_along()
:
seq_len(5) #> [1] 1 2 3 4 5 n <- 0 seq_len(n) #> integer(0)
“integer(0)
” is a length zero sequence of integers (not a sequence containing the value zero).
jmount
Data Scientist and trainer at Win Vector LLC. One of the authors of Practical Data Science with R.
I may be missing something, but I don’t understand why you call the result of 1:0 a bug. That produces exactly what I would expect it to.
The “:” operator can be thought of as shorthand for a call to the seq() function. This shorthand only accepts 2 of the arguments that seq() accepts—“from” and “to”—default values are used for the others. Note that seq(1,0) returns the same result that 1:0 does. To get an empty string, just issue seq(length.out=0).
Two R results that I mentioned:
Thanks for your comment. I left a bit out which I have now put at the end of the article with an edit. Sorry about that.
The behavior does match
help(`:`)
, so it isn’t a bug on its own. But it is a key part of buggy for-loops such as this:From https://www.r-bloggers.com/writing-a-for-loop-in-r/:
The above errors out if
foo
has length 0. And the above form is unfortunately taught a lot. The fix I suggest is useseq_len()
. Yes, one can usevapply()
in this case.I wish I had said it in the original article, but code of the form
1:length(x)
is a very common (so common I forgot I had not mentioned it), and this the use of “:
” and is often wrong (not what the programmer actually intended).Agree that bug in the headline is misleading. Maybe replace “bug” with “situation”.
Thanks! That is the idea that was not coming to me. I am going to say “trap” (still strong, but less misleading).
That sounds good to me! Thanks for your articles.
for(i in 1:5) {
+ print(paste(i, i*i))
+ }
produces-
[1] “1 1”
[1] “2 4”
[1] “3 9”
[1] “4 16”
[1] “5 25”
omitting the last line makes me worry that you wrote this in python :)
Thank you. And sorry about that (now fixed).
And that is why I should have used RMarkdown instead of copying and pasting!
Turns out we are all in good company:
(Quoted from the fortunes package.)