A.ExRange (Aja v0.4.3) View Source

Exclusive ranges are an exact copy of regular ranges (see Range), except that they exclude the second parameter.

Why/when would you need exclusive ranges?

The most typical use case would be when using 1..n for loops based on a parameter n >= 0:

iex> incorrect = fn n -> for i <- 1..n, do: "id_#{i}" end
iex> incorrect.(3) # works fine in general...
["id_1", "id_2", "id_3"]
iex> incorrect.(0)  # weird bug at edge case!
["id_1", "id_0"]

To fix it, you would typically need to match the n == 0 case and handle it differently, which is adds noise. With exclusive ranges like 0 ~> n, you get to keep the compact and elegant approach from above, while having a correct algorithm that covers the edge case:

iex> correct = fn n -> for i <- 1 ~> n + 1, do: "id_#{i}" end
iex> correct.(3) # works fine
["id_1", "id_2", "id_3"]
iex> correct.(0)  # edge case works fine too
[]

Exclusive ranges can be either increasing (start <= stop) or decreasing (start > stop). The start parameter is included (except if start == stop), the stop parameter is always excluded.

An exclusive range is represented internally as a struct A.ExRange{start: start, stop: stop} and can be used as is.

The A.~>/2 convenience macro makes it possible to have a more compact syntax, similar to ../2. It is totally optional and needs to be imported:

iex> import A
iex> import A, only: [~>: 2]  # more selective

Examples:

iex> A.ExRange.new(5)
#A<0 ~> 5>
iex> range = 0 ~> 5
#A<0 ~> 5>
iex> start ~> stop = range
iex> {start, stop}
iex> {0, 4}
iex> Enum.to_list(range)
[0, 1, 2, 3, 4]
iex> Enum.count(range)
5
iex> Enum.member?(range, 5)
false
iex> Enum.member?(range, 4)
true
iex> Enum.to_list(3 ~> 0)
[3, 2, 1]

Just like Ranges, 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 exclusive range. start defaults to 0.

Link to this section Types

Specs

t() :: %A.ExRange{start: integer(), stop: integer()}

Link to this section Functions

Link to this function

disjoint?(range1, range2)

View Source

Specs

disjoint?(t(), t()) :: boolean()

Checks if two ranges are disjoint.

Examples

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

Specs

new(integer(), integer()) :: t()

Creates a new exclusive range. start defaults to 0.

Examples

iex> A.ExRange.new(0, 100)
#A<0 ~> 100>
iex> A.ExRange.new(10)
#A<0 ~> 10>