Mix.install([
{:jason, "~> 1.4"},
{:kino, "~> 0.9", override: true},
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_cell"}
])
This is a guest post created by Quentin Crain and inspired by Jeff Helman, one of our fantastic beta-testers.
In the lesson on creating a range of numbers using the ..
operator:
iex > Enum.to_list(1..5)
[1, 2, 3, 4, 5]
You also learned that it can optionally take a step value:
iex > Enum.to_list(1..5//2)
[1, 3, 5]
In this way, the programmer may create a
sequence of integers starting at the first number and
returning increments by step
until the last number
is reached or exceeded.
But, what happens when the step attempts to create a sequence that is not incrementing towards the last number, such as:
iex> Enum.to_list(1..5//-1)
[]
In this case, an empty list ([]
) is returned as there
are no numbers between 1
and 5
starting at 1
and
incrementing by -1
.
As was brought up* in a Beta release of this course, why would Elixir allow such code? Does it compile? Does it evaluate?
In this bonus exercise, you will be recreating the range function AND creating a safe version.
In Elixir, there is already a standard on providing safe versions
of functions: When you want Elixir to throw an exception instead
of returning a reasonable or error value, the name of the function
will end with !
.
Two examples from the standard library may be referred to:
Enum.fetch
& Enum.fetch!
The Enum module provides two functions to retrieve an element from a list ([]
) at a given index. The difference is in how
they behave when the index is invalid; ex:
iex> list = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex> Enum.fetch(list, 10)
:error
iex> Enum.fetch!(list, 10)
** (Enum.OutOfBoundsError) out of bounds error
(elixir 1.14.0) lib/enum.ex:1072: Enum.fetch!/2
iex:27: (file)
Map.fetch
& Map.fetch!
When accessing a Map (%{}
) with a key, the Map module
provides these two functions. The difference is in how they
behave when the key is not present in the map; ex:
iex> map = %{a: 1, b: 2}
%{a: 1, b: 2}
iex> Map.fetch(map, :c)
:error
iex> Map.fetch!(map, :c)
** (KeyError) key :c not found in: %{a: 1, b: 2}
(stdlib 4.0.1) :maps.get(:c, %{a: 1, b: 2})
iex:29: (file)
Your assignment is to implement the SafeRange
module with 2
functions called range
and range!
where:
-
SafeRange.range
behaves as the existing..//STEP
function -
SafeRange.range!
verifies that the given step "makes sense" with the given first and last values, and if not a RuntimeError exception is thrown.
In Elixir exceptions may be thrown using the raise
function,
for example here is how to throw a RuntimeError exception:
iex> raise "Houston, we have a problem."
** (RuntimeError) Houston, we have a problem.
defmodule SafeRange do
@doc """
Generate a sequences of integers from 'first' to 'last' with an
option 'step' value.
If incrementing by 'step' will not end at or beyond 'last',
just return an empty list ([]).
## Examples
iex> SafeRange.range(1, 5)
[1, 2, 3, 4, 5]
iex> SafeRange.range(1, 5, 2)
[1, 3, 5]
iex> SafeRange.range(1, 5, -1)
[]
iex> SafeRange.range(5, 1, 1)
[]
"""
def range(first, last, step \\ DEFAULT) do
end
@doc """
Generate a sequences of integers from 'first' to 'last' with an
option 'step' value.
If incrementing by 'step' will not end at or beyond 'last',
a "RuntimeError" will be thrown.
## Examples
iex> SafeRange.range!(1, 5)
[1, 2, 3, 4, 5]
iex> SafeRange.range!(1, 5, 2)
[1, 3, 5]
iex> SafeRange.range!(1, 5, -1)
** (RuntimeError) Invalid step value of: -1
iex> SafeRange.range!(5, 1, 1)
** (RuntimeError) Invalid step value of: 1
"""
def range!(first, last, step \\ DEFAULT) do
end
end
Example Solution
defmodule SafeRange do
def range(first, last, step \\ 1) do
Enum.to_list(first..last//step)
end
def range!(first, last, step \\ 1) do
cond do
step < 0 and first < last ->
raise "Invalid step value of: #{step}"
step > 0 and first > last ->
raise "Invalid step value of: #{step}"
true -> Enum.to_list(first..last//step)
end
end
end
DockYard Academy now recommends you use the latest Release rather than forking or cloning our repository.
Run git status
to ensure there are no undesirable changes.
Then run the following in your command line from the curriculum
folder to commit your progress.
$ git add .
$ git commit -m "finish SafeRange exercise"
$ git push
We're proud to offer our open-source curriculum free of charge for anyone to learn from at their own pace.
We also offer a paid course where you can learn from an instructor alongside a cohort of your peers. We will accept applications for the June-August 2023 cohort soon.