The following covers some common tricks for golfing R on code.golf. In general it is recommended to check out the documentation pages as well (Type ?name into an R REPL or view the online version). Many functions have a collection of optional parameters that can come in useful at times.
While this guide tries to be somewhat complete, you might find some additional ideas on "Tips for golfing in R" on CGSE. Also browse that site for public solutions in R.
Looping
The usual for(x in ...) is usually used, but array manipulation is often used to golf bytes off. For example:
for(x in commandArgs(T))write(x*50,1)
write(commandArgs(T)*50,1,1)
If explicit loops are required, sometimes Map, Reduce, lapply and related functions can be shorter than a for loop.
Strings
Type Conversion
Some examples of using coercion to avoid using long functions:
as.integer("19")
strtoi("19")
("19":1)[1]
el("19":1)
x="19";x:x
# x:x also often replaces x[1] to get the first element of a list of integers
as.character(19)
x=19;x[0]="";x
c("",19)[2]
paste(19)
Splitting Strings
For parsing a sequence of numbers from a single input string, scan is quite useful:
# numbers separated by whitespace
scan(t="1 2\n3")
# other separator
scan(t="1,2,3",se=",")
Depending on the scenario, you can add extra commas to pad out the arguments. This saves bytes only if there are other arguments besides sep, and works for both scan and write.
write(x,1,se=",")
write(x,1,,,",")
If you need to split a string into multiple strings:
# separated by whitespace
scan(,"",t="abc def") # "" can be any string
# other separator
el(strsplit("abc,def",","))
scan(,"","abc,def",se=",")
Iterating over characters
If you need to iterate over the characters in a string, it is often advantageous to work with Unicode code points instead:
y=utf8ToInt("the lazy fox ...")
# transform y, then back to string:
cat(intToUtf8(y))
Otherwise you can split the string into characters (see above) and join back into a string:
Reduce(paste0,c("a","b","c"))
paste0(c("a","b","c"),collapse="")
# or, ideally print without joining:
write(c("a","b","c"),1,9,,"")
cat(c("a","b","c"),"\n",sep="")
Regex
Regular expressions are quite useful because they can be directly applied to strings without first needing to split them.
gsub and grepl are probably the most useful, see the docs page for a full list of available functions.
Output
There are multiple functions commonly used to produce output:
# write the values of X to STDOUT (1)
# for strings, this writes one value per line, for numbers multiple per line
write(X,1)
# write the values of X to STDOUT, 2 per line, separated by a comma
write(X,1,2,,",")
# conditionally write the values of X to
# STDOUT (1), if cond is FALSE
# STDERR (2), if cond is TRUE, these values are ignored by the judge on this site
write(X,1+cond)
# same idea, inverted condition:
write(X,2-cond)
# cat does not implicitly print a newline, and takes multiple arguments to print
# by default it separates values by a space
cat(1:4,"
")
# cat has some keyword-only parameters:
cat(...,fill=1) # print one value per line
cat(...,sep=",") # separate values by comma
# dput prints a representation of the value to STDOUT, and returns the value
y=2*dput(x)
# this is especially useful for printing integers, but you might need to convince the value to have the right internal type:
dput(0+x)
# Because dput returns a value that can be used in a boolean context, it allows for this form of conditional output:
cond&&dput(x)
When outputting with paste0 to join a vector into a string by nothing:
write(paste0(vector),1)
cat(vector,"
",sep="")
If you are outputting three args to cat, you can abuse the sep function, but note that only strings can be used in it and that x and y cannot be vectors and have to be strings or this won't work:
cat(x,"/",y,sep="")
cat(x,y,sep="/")
Conditional statements
The if statements that are commonly used:
"if"(cond,x,y)
c(y,x)[1+(cond)] # often equivalent: c(y,x)[2-!cond]
"if" usually beats using vector indexing because you don't need to put >0, and using a list requires turning the condition into an integer and adding 1 since R uses base 1-index vectors.
Vector indexing is occasionally used though in replacement of rep. Say we want to print a string if a condition is true:
rep(str,cond)
str[cond]
Using [] will ensure that if the condition is true it will return the entire string and if not nothing. This works with multiple items too:
cat(paste0(rep(c("o",str),cond)))
cat(paste0(c("o",str)[cond]))
cat(c("o",str)[cond],sep="")
For conditionally printing one value, see the Output section.
Builtins
R, as a statistical language, has tons of builtins and functions that are very useful when golfing.
Sometimes these can be creatively repurposed, for example: !sd(x) is TRUE if all values in x are the same.
Here are is a list of potentially useful builtins:
seq
diff
rle # Run Length Encoding
embed
gl # Generate factors by specifying the pattern of their levels
letters # 'a','b','c'...
LETTERS # 'A','B','C'...
month.abb # 'Jan','Feb','Mar'...
month.name # 'January','February','March'...
T, F # TRUE, FALSE
filter # 1-d convolution & autoregressive filter
cumsum
T and F can be used as variables that are already initialized to 1 or 0 at the start of your code.
Aliases
We can define short aliases for functions using backticks for two arguments using primitive operators:
`+`=paste
x+y # paste(x,y)
(There is a way to define a short alias for functions with 3 or more arguments)
The defined aliases will inherit the precedence/parsing rules from the symbols they are defined to; an overview of the precedences can be found on the Syntax page.
Choosing the right symbol can therefore minimize the number of required parentheses. One particularly interesting case is ?, which can be called with one or two arguments and binds lower than almost anything else in the language, apart from punctuation like ,, ; and newlines. One use case is aliasing Map if you have a lot of loops (you need to avoid the implicit output though):
`?`=Map
\(x)do_stuff(x)?1:10 # parses as `?`(\(x)do_stuff(x),1:10)
# vs
for(x in 1:10)do_stuff(x)
Arrays
While R allows writing solutions like in other languages with for and while loops or recursive functions, it can be shorter in some cases to use R's array processing capabilities instead.
x%o%y computes the outer product of two arrays, basically a multiplication table for two vectors. outer is the generalized version of this where you can replace the multiplication with any other arithmetic operation:
> x=1:3
> x%o%x
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 2 4 6
[3,] 3 6 9
> outer(x,x,"+")
[,1] [,2] [,3]
[1,] 2 3 4
[2,] 3 4 5
[3,] 4 5 6
%*% implements matrix multiplication. Besides the usual matrix-matrix and matrix-vector product, it also computes the inner product for two vectors: x%*%y == sum(x*y). Syntactically these operators (and %x% for Kronecker product) bind with the same strength as %% and %/%, only x^y and unary -x and +x bind tighter among arithmetic operations.
Applying normal arithmetic operations between two vectors of different lengths cycles the shorter vector to match the other length:
> 1:10 * 0:1
[1] 0 2 0 4 0 6 0 8 0 10
As an exercise, using these techniques it is possible to do the following in 15 bytes:
Given a single-letter variable holding 1:n, compute a vector containing $\sigma(i), i=1..n$, the sums of divisors of these numbers
> x=1:10
> ???????????????
[1] 1 3 4 7 6 12 8 15 13 18
Many sequence holes have solutions that look like write(...,x<-1:100,1).
rep() alternatives
Even though rep() is already fairly short, we can save a few more bytes by using : and vector recycling.
Repeating n zeroes (n>0) - !1:n or 0*1:n instead of rep(0,n)
Repeating n ones - !!1:n instead of rep(1,n)
Repeating x n times - x+!1:n instead of rep(x,n)
Using modular arithmetic to avoid each args in rep
There aren't too many uses of this, but when you encounter one, you will probably need some creativity to come up with some "formula".
Generating the vector c(-1,-1,-1,0,0,0,1,1,1) is easily done with rep(-1:1,e=3), but you can use modular arithmetic to save bytes:
-3:5%/%3
Datasets
Random Tips
- Use
<- instead of = to remove brackets, so while(x<-...) instead of while((x=...))
-> exists as well and can save 1 byte in for(x in r<-!0:9) -> for(x in!0:9->r)
- Length of a (numeric) vector:
sum(v|1) instead of length(x)
- Last element of vector:
tail(x,1) instead of x[length(x)]
- Negative indices remove values from a vector:
x[-1] is x without the first element
- Floor:
x%/%1 instead of floor(x)
"for"(,0:9,...) is a shorter way to loop n times if you don't need the variable
Packers
3:1
eval(parse(t=intToUtf8(rep(utf8ToInt("..."),e=3)%%97:99+32)))
2:1
eval(parse(t=iconv("...",,"ucs2")))