[1] 1.1
[1] 2
[1] 8.988466e+307
[1] 1
[1] 65535
Chapter 6: R Syntax
Shih Chien University
2026-08-03
Chapter 5 showed that every R expression can be rewritten as a function call. Yet R provides special syntax — syntactic sugar — so that assignments, lookups, and arithmetic read naturally. (Without it, R code would look like LISP, all parentheses; no coincidence, since S was inspired by LISP.)
This chapter is a readable, not exhaustive, guide to writing valid R expressions:
Numbers are interpreted literally — and hexadecimal works with the 0x prefix:
By default every number — even a plain integer — is stored as a double-precision floating-point value. To get a true integer, use sequence notation or as:
a:b yields the integers from a to b; arbitrary sets combine with c:
Flexibility has limits — doubles cannot distinguish or represent everything:
In practice this rarely matters: the databases your data comes from can’t represent such numbers either.
Complex values are written as real part + imaginary part i:
[1] -1+0i
[1] 0+1i
[1] -1+1.224647e-16i
Warning in sqrt(-1): 產生了 NaNs
[1] NaN
Note the last line: handed the numeric -1, sqrt answers in numerics — and the numeric answer to √−1 is NaN, with a warning. Handed a complex number, it answers in complex.
A character object is the text between a pair of quotes — double or single, your choice, or escaped with backslashes:
[1] "hello"
[1] "hello"
[1] TRUE
[1] TRUE
Single quotes are convenient when the text itself contains double quotes (and vice versa). Longer vectors, as always, come from c:
A symbol is a constant that refers to another object — the name of a variable. x <- 1 means “map the symbol x to the value 1 in the current environment.”
Names that start with a letter and contain letters, digits, periods, and underscores can be typed directly — and case matters:
Symbols with special syntax are reached by enclosing them in backquotes — that is how you get help on the assignment operator (?`<-`), and, if you insist, how you create perverse names:
Some words are reserved and cannot be used as symbols: if, else, repeat, while, function, for, in, next, break, TRUE, FALSE, NULL, Inf, NaN, NA, NA_integer_, NA_real_, NA_complex_, NA_character_, ..., and ..1 through ..9.
Primitive functions not on the reserved list can be redefined — even c:
function (...) .Primitive("c")
[1] 1
[1] 1 2 3
R can tell from context that c(1, 2, 3) needs a function named c, and finds the original. Clever — but shadowing built-in names is still a recipe for confusion; avoid it in real code.
An operator is a function of one or two arguments that can be written without parentheses:
[1] 20
[1] 20
[1] 20
[1] 20
[1] 10
Unary operators take a single operand — negation (-7) and help (?) are familiar examples.
A user-defined binary operator is any function of two variables assigned to a name of the form %text%:
Some core language constructs are themselves binary operators: assignment (symbol on the left, value on the right), indexing (symbol left, index right), and even function calls (function left, arguments right — don’t be fooled by the closing bracket or parenthesis):
Just as in school math (1 + 2 × 5 multiplies first), R resolves ambiguity with fixed precedence rules. From highest to lowest, roughly: function calls and grouping; indexing and lookup; arithmetic; comparison; formulas; assignment; help.
| Operators (by priority) | Description |
|---|---|
( { |
Function calls, grouping |
:: ::: |
Namespace access |
$ @ |
Component / slot extraction |
[ [[ |
Indexing |
^ |
Exponentiation (right to left) |
- + |
Unary minus, plus |
: |
Sequence |
%any% |
Special operators |
* / |
Multiply, divide |
+ - |
Binary add, subtract |
< > <= >= == != |
Ordering, comparison |
! |
Negation |
& && |
And |
| || |
Or |
~ |
Formulas |
-> ->> |
Rightward assignment |
<- <<- |
Assignment (right to left) |
= |
Assignment (right to left) |
? |
Help |
The authoritative, current list: help(syntax).
Most assignments simply bind an object to a symbol:
But R also allows a function on the left-hand side — replacing an object with a modified version of itself:
The magic behind the scenes: a statement of the form fun(sym) <- val is syntactic sugar for
which replaces the object bound to sym in the current environment. By convention fun names a property of the object. Define your own method named myproperty<-, and R lets you write myproperty(x) <- value — your own left-hand-side function.
Expressions go on separate lines, or on one line separated by semicolons:
Parentheses return the result of the expression inside — precedence-wise the same as a function call. Indeed, grouping is equivalent to calling an identity function:
Curly braces evaluate a series of expressions (newline- or semicolon-separated) and return only the last:
A crucial difference: a function call creates a new environment; braces do not — their contents evaluate in the current environment:
Internally, brace notation is a call to the `{` function — though its arguments are not evaluated like a standard function’s. (Scope and environments: Chapter 8.)
The forms are if (condition) true_expression else false_expression and if (condition) expression. Because the branches are not always evaluated, if has type special:
An if condition is not a vector operation. In the book’s era a longer condition used the first element with a warning; modern R (≥ 4.2) makes it an error:
For an element-wise choice, use ifelse:
Returning different values for different inputs via chained else ifs is verbose:
switch does the same job tidily — named arguments map inputs to results; the unnamed argument is the default:
R has three looping constructs. The simplest, repeat, just repeats an expression — forever, unless you break (and next skips to the next iteration). Multiples of 5 up to 25:
Forget the break and you have an infinite loop — occasionally useful for interactive applications, otherwise a bug.
while (condition) expression repeats as long as the condition holds; break and next work here too:
for (var in list) expression iterates over the elements of a vector or list:
Two properties to remember:
[1] 25
Like conditionals, repeat, while, and for have type special — their body is not necessarily evaluated.
Note
Missing Java-style iterators or foreach? CRAN add-on packages supply both — and they make code easier to parallelize.
iterators: iter(obj, checkFunc=..., recycle=...) builds an iterator; nextElem fetches values (filtered by checkFunc) until it stops with “StopIteration”:
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
Error:
! StopIteration
foreach: builds a loop object, evaluated by the %do% (serial) or %dopar% (parallel — Chapter 26) operators:
| Syntax | Objects | Description |
|---|---|---|
x[i] |
vectors, lists | Elements described by i — an integer, character (names), or logical vector. No partial matching. On a list returns a list; on a vector, a vector. |
x[[i]] |
vectors, lists | A single element matching i (integer or character of length 1). Partial name matching with exact=FALSE. |
x$n |
lists | The element named n. |
x@n |
S4 objects | The value in slot n. |
Single vs. double brackets, three differences: [[ returns exactly one element while [ may return several; by name, [ matches exactly while [[ allows partial matches; on lists, [ returns a list but [[ returns the element itself.
[1] 104
[1] 100 101 102 103 104
[1] 100 105 110 115
[1] 102
Negative integers exclude the named positions:
The same notation applies to lists:
For multidimensional structures, give one index per dimension — or omit one to take everything along it:
[,1] [,2] [,3] [,4]
[1,] 101 104 107 110
[2,] 102 105 108 111
[3,] 103 106 109 112
[1] 103
[1] 112
[,1] [,2]
[1,] 101 104
[2,] 102 105
[,1] [,2] [,3] [,4]
[1,] 101 104 107 110
[2,] 102 105 108 111
[,1] [,2]
[1,] 107 110
[2,] 108 111
[3,] 109 112
Subsets are automatically coerced to the fewest sensible dimensions: a matrix-shaped subset returns a matrix, a vector-shaped one a vector. Disable with drop=FALSE:
The same notation assigns — single cells, blocks, even positions beyond the current end (gaps become NA):
[,1] [,2] [,3] [,4]
[1,] 1001 1003 107 110
[2,] 1002 1004 108 111
[3,] 103 106 109 112
[1] 1 2 3 4 5 6 7 8 9 10 11 12 NA NA 15
A factor used as an index is interpreted as an integer vector.
A logical vector selects the TRUE positions — and is recycled if shorter than the target:
[1] 100 102 104 106 108 110 112 114 116 118
[1] 103
[1] 102 105 108 111 114 117
[1] 100 103 106 109 112 115 118
Lists too:
List elements may be named; index them with $, or by name vectors in single brackets:
[1] 10
$a
[1] 1
$b
[1] 2
$c
[1] 3
Double brackets take a single name — and uniquely allow partial matching with exact=FALSE:
For lists of lists, [[ accepts a vector and walks down the levels:
Style isn’t syntax, but it decides whether others (and future you) can read your code. The book follows Google’s R Style Guide; the highlights:
{ not on its own line; closing brace } on its own line; indent inner blocks two spaces.<-, not =.And no — you are not obliged to name your objects field.goals or top.bacon.searching.cities. It’s just convention. (The modern community mostly follows the tidyverse style guide, which prefers snake_case — pick one style and stay consistent.)
Copyright. These slides are adapted from R in a Nutshell: A Desktop Quick Reference (2nd ed.) by Joseph Adler, O’Reilly Media. All rights reserved by the original author and publisher.
Non-commercial use only. These materials are strictly for educational purposes and may not be used for commercial gain.
Attribution. Any reproduction, distribution, or use of these materials must properly credit the original source.
R in a Nutshell: A Desktop Quick Reference