View Source Range (Elixir v1.13.0)
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(first, last) :: %Range{first: first, last: last, step: step()}
Link to this section Functions
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
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
Creates a new range with step
.
Examples
iex> Range.new(-100, 100, 2)
-100..100//2
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