View Source Range (Elixir v1.13.1)

Ranges represent a sequence of zero, one or many, ascending or descending integers with a common difference called step.

Ranges are always inclusive and they may have custom steps. The most common form of creating and matching on ranges is via the first..last and first..last//step notations, auto-imported from Kernel:

iex> Enum.to_list(1..3)
[1, 2, 3]
iex> Enum.to_list(1..3//2)
[1, 3]
iex> Enum.to_list(3..1//-1)
[3, 2, 1]

Ranges may also have a single element:

iex> Enum.to_list(1..1)
[1]
iex> Enum.to_list(1..1//2)
[1]

Or even no elements at all:

iex> Enum.to_list(10..0//1)
[]
iex> Enum.to_list(0..10//-1)
[]

When defining a range without a step, the step will be defined based on the first and last position of the range, If first >= last, it will be an increasing range with a step of 1. Otherwise, it is a decreasing range. Note however implicitly decreasing ranges are deprecated. Therefore, if you need a decreasing range from 3 to 1, prefer to write 3..1//-1 instead.

Definition

An increasing range first..last//step is a range from first to last increasing by step where step must be a positive integer and all values v must be first <= v and v <= last. Therefore, a range 10..0//1 is an empty range because there is no value v that is 10 <= v and v <= 0.

Similarly, a decreasing range first..last//step is a range from first to last decreasing by step where step must be a negative integer and values v must be first >= v and v >= last. Therefore, a range 0..10//-1 is an empty range because there is no value v that is 0 >= v and v >= 10.

Representation

Internally, ranges are represented as structs:

iex> range = 1..9//2
1..9//2
iex> first..last//step = range
iex> first
1
iex> last
9
iex> step
2
iex> range.step
2

You can access the range fields (first, last, and step) directly but you should not modify nor create ranges by hand. Instead use the proper operators or new/2 and new/3.

A range implements the Enumerable protocol, which means functions in the Enum module can be used to work with ranges:

iex> range = 1..10
1..10
iex> Enum.reduce(range, 0, fn i, acc -> i * i + acc end)
385
iex> Enum.count(range)
10
iex> Enum.member?(range, 11)
false
iex> Enum.member?(range, 8)
true

Such function calls are efficient memory-wise no matter the size of the range. The implementation of the Enumerable protocol uses logic based solely on the endpoints and does not materialize the whole list of integers.

Link to this section Summary

Functions

Checks if two ranges are disjoint.

Creates a new range.

Creates a new range with step.

Returns the size of range.

Link to this section Types

@type limit() :: integer()
@type step() :: pos_integer() | neg_integer()
@type t() :: %Range{first: limit(), last: limit(), step: step()}
@type t(first, last) :: %Range{first: first, last: last, step: step()}

Link to this section Functions

Link to this function

disjoint?(range1, range2)

View Source (since 1.8.0)
@spec disjoint?(t(), t()) :: boolean()

Checks if two ranges are disjoint.

Examples

iex> Range.disjoint?(1..5, 6..9)
true
iex> Range.disjoint?(5..1, 6..9)
true
iex> Range.disjoint?(1..5, 5..9)
false
iex> Range.disjoint?(1..5, 2..7)
false

Steps are also considered when computing the ranges to be disjoint:

iex> Range.disjoint?(1..10//2, 2..10//2)
true

# First element in common in all below is 29
iex> Range.disjoint?(2..100//3, 9..100//5)
false
iex> Range.disjoint?(101..2//-3, 99..9//-5)
false
iex> Range.disjoint?(1..100//14, 8..100//21)
false
iex> Range.disjoint?(57..-1//-14, 8..100//21)
false
iex> Range.disjoint?(1..100//14, 51..8//-21)
false

# If 29 is out of range
iex> Range.disjoint?(1..28//14, 8..28//21)
true
iex> Range.disjoint?(2..28//3, 9..28//5)
true
@spec new(limit(), limit()) :: t()

Creates a new range.

If first is less than last, the range will be increasing from first to last. If first is equal to last, the range will contain one element, which is the number itself.

If first is greater than last, the range will be decreasing from first to last, albeit this behaviour is deprecated. Therefore, it is advised to explicitly list the step with new/3.

Examples

iex> Range.new(-100, 100)
-100..100
Link to this function

new(first, last, step)

View Source (since 1.12.0)
@spec new(limit(), limit(), step()) :: t()

Creates a new range with step.

Examples

iex> Range.new(-100, 100, 2)
-100..100//2
Link to this function

size(range)

View Source (since 1.12.0)

Returns the size of range.

Examples

iex> Range.size(1..10)
10
iex> Range.size(1..10//2)
5
iex> Range.size(1..10//3)
4
iex> Range.size(1..10//-1)
0

iex> Range.size(10..1)
10
iex> Range.size(10..1//-1)
10
iex> Range.size(10..1//-2)
5
iex> Range.size(10..1//-3)
4
iex> Range.size(10..1//1)
0