iTranslated by AI
Language-Specific Syntax: R
This article is for the 22nd day of the Programming Language Specific Syntax Advent Calendar 2025.
I will introduce them along with some of my personal preferences.
Sample code for binary search
Implemented by intentionally using language-specific features.
binary_search <- function(arr, target) {
left <- 1
right <- length(arr)
while (left <= right) {
mid <- (left + right) %/% 2
if (arr[mid] == target) {
return(mid)
} else if (arr[mid] < target) {
left <- mid + 1
} else {
right <- mid - 1
}
}
return(NA)
}
arr <- c(1, 3, 5, 7, 9)
result <- binary_search(arr, 5)
print(ifelse(is.na(result), -1, result)) # 3 (R is 1-indexed)
Since it's a language designed for statistical analysis, the code didn't end up looking very idiomatic for R.
Featured Syntax
Special Assignment Operators
There are special assignment operators, such as for right-to-left assignment or assignment to a parent scope.
# Right to left
x <- 10
x = 10 # Same
# Left to right
10 -> x
# Super assignment (assign to parent scope)
f <- function() {
x <<- 100 # Assign globally
}
Left-to-right assignment with -> and assignment to the global scope with <<- are unique to R.
Indexing (1-indexed)
Array indexing starts from 1, and you can exclude elements using negative indices.
arr <- c(10, 20, 30, 40, 50)
# Single element
arr[1] # 10 (First element)
arr[length(arr)] # 50 (Last)
# Range
arr[2:4] # c(20, 30, 40)
# Negative index = Exclude
arr[-1] # c(20, 30, 40, 50)
arr[c(-1, -2)] # c(30, 40, 50)
# Logical index
arr[arr > 25] # c(30, 40, 50)
Negative indices excluding elements from the start is the opposite of slicing and can be confusing.
Vector Operations
Operations can be applied to an entire vector at once.
# Operations on the entire vector
x <- c(1, 2, 3, 4, 5)
x * 2 # c(2, 4, 6, 8, 10)
x + c(10, 20, 30, 40, 50)
# Broadcasting
x + 10 # c(11, 12, 13, 14, 15)
# Logical operations
x > 3 # c(FALSE, FALSE, FALSE, TRUE, TRUE)
x[x > 3] # c(4, 5) Filtering
# Vectorized functions
sqrt(x)
sum(x)
mean(x)
It is similar to NumPy.
Pipe Operators
You can chain processes by passing the result on the left to the function on the right.
# |> (R 4.1+) or %>% (magrittr)
result <- c(1, 2, 3, 4, 5) |>
(\(x) x * 2)() |>
sum()
# 30
# tidyverse style
library(dplyr)
data |>
filter(age > 18) |>
select(name, age) |>
arrange(desc(age))
Function Definitions
You can define functions where the last expression automatically becomes the return value.
# Basic form
add <- function(a, b) {
a + b # The last expression is the return value
}
# Default arguments
greet <- function(name, greeting = "Hello") {
paste(greeting, name)
}
# Variable length arguments
sum_all <- function(...) {
args <- list(...)
Reduce(`+`, args, 0)
}
Its expression-oriented nature makes return unnecessary, and the language surprisingly provides a good variety of argument handling features.
Lists and Data Frames
Lists that can store different types and tabular data frames.
# Lists (Heterogeneous)
person <- list(name = "Alice", age = 30, scores = c(80, 90, 85))
person$name # "Alice"
person[["age"]] # 30
person$scores[1] # 80
# Data frames
df <- data.frame(
name = c("Alice", "Bob"),
age = c(30, 25)
)
df$name # Column access
df[1, ] # Row access
df[df$age > 25, ] # Filter
As expected, the syntax for data access is quite comprehensive.
Discussion