iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔡

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.

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.

GitHubで編集を提案

Discussion