# Joy of Elixir

## Exercise Solutions

This section contains solutions for all the exercises in this book. Use this only if you're stuck!

### Chapter 1

#### Exercise 1

Get Elixir to calculate the number of seconds in the day by multiplying the hours in a day by 60 twice. How many seconds are there in a day?

There are 24 hours in a day, so we can multiply by 60 twice like this to get our answer:

``````iex> 24 * 60 * 60
86400``````

#### Exercise 2

Calculate the average of these numbers: 4, 8, 15, 16, 23 and 42.

To calculate the average of these numbers, we need to add them all together and then divide that number by how many numbers there are, so 6.

``````iex> 4 + 8 + 15 + 16 + 23 + 42
108
iex> 108 / 6
18.0``````

Special thing to note here is that when we divide in Elixir, it will always give us a decimal representation of the number, rather than a whole number. This is why we see `18.0` instead of simply `18` here.

### Chapter 2

#### Exercise 1

If we store the number of seconds in a day using this code: `seconds = 86400`, calculate using that variable how many hours there are in 30 days.

Tricky part here is that the variable is for seconds, but the answer we seek is for hours. To convert the number of seconds into hours, we need to a similar calculation to Exercise 1.1: take the number of seconds, divide by 60 to get minutes and then divide by 60 again to get how many hours are in a single day:

``````iex> seconds = 86400
86400
iex> seconds / 60 / 60
24.0``````

We know that there are 24 hours in a given day. So to get the number of hours in 30 days, we can multiply 24 by 30:

``````iex> 24.0 * 30
720``````

This gives us our answer: there are 720 hours in 30 days. Doing this whole calculation in a single line would look like:

``````iex> seconds / 60 / 60 * 30
720``````

It's possible to make this a little shorter too. At the end of this line, we're dividing by 60 and then multiplying by 30. Dividing anything by 60 and multiplying it by 30 is the same as dividing it by 2 (2 comes from dividing 60 by 30). The same could be said if we divided by 10 and multiplied by 5, or divided by 4 and multiplied by 2. So we can shorten this by dividing by 60 once, and then dividing by 2 to get the same answer:

``````iex> seconds / 60 / 2
720``````

#### Exercise 2

The line `5 / "four"` shows an error. Think about why this error might happen.

The error that we'll see for this line is this:

``````iex> 5 / "four"
** (ArithmeticError) bad argument in arithmetic expression: 5 / "four"
:erlang./(5, "four")``````

Elixir is saying here that there's a "bad argument in arithmetic expression" -- which is a confounding way to say "what you're asking me to say doesn't make sense to do!". It is not possible to divide the number 5 by the word "four". These two things are incompatible, which is why we're seeing this issue.

### Chapter 3

There are no exercises for this chapter.

### Chapter 4

There are no exercises for this chapter.

### Chapter 5

#### Exercise 1

Make a function which turns fahrenheit temperatures into celsius.

Early on in Chapter 5, we see a function which converts celsius into fahrenheit:

``iex> c_to_f = fn (c) -> c * 1.8 + 32 end``

This exercise is about going the other way: converting from fahrenheit to celsius. We need to reverse the order of operations here and then turn the operations into their opposites too. Where we add, we minus. Where we multiply, we divide:

``iex> f_to_c = fn (f) -> (f - 32) / 1.8 end``

We must include the brackets here on the first operation, otherwise Elixir will attempt to divide 32 by 1.8 first. Go on, try it without the brackets and see what happens.

We can run this function like this:

``````iex> f_to_c.(104)
40
``````

If you ask Google "104 Farenheit in Celsius?", you'll see this is the right result: Google result for '104 Farenheit in Celsius'

Other numbers should work just as well too!

#### Exercise 2

Make a function which returns the number of seconds in the specified amount of days. For example, `seconds.(2)` should tell us how many seconds there are in 2 days.

We know that there are 24 hours in a day, 60 minutes in an hour, and 60 seconds in an hour. So to get from days to seconds, we need to multiply by 24, then 60, then 60 again. In a function, this would look like:

``iex> seconds = fn (days) -> days * 24 * 60 * 60 end``

We can then run this function like this:

``````iex> seconds.(1)
86400
iex> seconds.(2)
172800
``````

#### Exercise 3

Make a function which takes two maps with `"age"` keys in them and returns the average age.

This function needs to take two maps, and the easiest way to do that would be to have them as two separate arguments.

``iex> average_age = fn (person_1, person_2) -> ...``
We can then take out the relevant `age` values from these maps with some pattern matching:

``iex> average_age = fn (%{"age" => age_1}, %{"age" => age_2}) -> ...``

This change of the function makes it so that the function only cares about the `"age"` keys in the maps, and will ignore the rest. From there, we need to calculate the average. The way we can do that is to add together all the numbers we have, and then divide the result by the count of the things we added together.

``iex> average_age = fn (%{"age" => age_1}, %{"age" => age_2}) -> (age_1 + age_2) / 2 end``

We can then run this function like this:

``````average_age.(%{"age" => 15}, %{"age" => 45})
30.0``````

It's important to note that both of the maps have to have the `"age"` key, otherwise this code will not work:

``````average_age.(%{"age" => 15}, %{"name" => "Izzy"})
** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/2

The following arguments were given to :erl_eval."-inside-an-interpreted-fun-"/2:

# 1
%{"age" => 15}

# 2
%{"name" => "Izzy"}``````

This error shows that the arguments that we passed do not match what the function expects. It helpfully shows us what we've passed, so that we can see for ourselves that the 2nd argument does not fit the criteria of needing an `"age"` key.

### Chapter 6

Make a function that takes either a map containing a "name" and "age", or just a map containing "name". Change the output depending on if "age" is present. What happens if you switch the order of the function clauses? What can you learn from this?

For this, we can use a function that pattern matches on what keys are in the map. We'll call this function `about` because it's going to tell us about a person:

``````iex> about = fn
%{"name" => name, "age" => age} ->
"#{name} is #{age} years old"
%{"name" => name} ->
"I don't know how old #{name} is!"
end``````

We can call the function with a map that contains a `"name"` and `"age"` key:

``iex> about.(%{"name" => "Ryan", "age" => 31})``

This map matches the first clause:

``````iex> about = fn
%{"name" => name, "age" => age} ->
"#{name} is #{age} years old"
%{"name" => name} ->
"I don't know how old #{name} is!"
end``````

Or we can call it with a map with just a name key:

``iex> about.(%{"name" => "Izzy"})``

This map matches the second clause, because the first clause requires both a `"name"` and `"age"` key.

``````iex> about = fn
%{"name" => name, "age" => age} ->
"#{name} is #{age} years old"
%{"name" => name} ->
"I don't know how old #{name} is!"
end``````

The last part of the exercise asks: "What happens if you switch the order of the function clauses?". Let's try it. We'll reorder the function:

``````iex> about = fn
%{"name" => name} ->
"I don't know how old #{name} is!"
%{"name" => name, "age" => age} ->
"#{name} is #{age} years old"
end``````

And then we'll try to run it:

``iex> about.(%{"name" => "Ryan", "age" => 31})``

This map matches the first clause because the first clause only needs a `"name"` key to match its pattern:

``````iex> about = fn
%{"name" => name} ->
"I don't know how old #{name} is!"
%{"name" => name, "age" => age} ->
"#{name} is #{age} years old"
end``````

This may be unexpected; it may be expected to match the second clause, the one with both `"name"` and `"age"`. But this will not happen, because the first clause is really relaxed: it only expects `"name"`

This is hopefully a good lesson in pattern matching in Elixir. A good rule to follow is that your clauses should be ordered from most-specific to least-specific. If you do not order your clauses like this, then you may have a clause matching before others unexpectedly.

### Chapter 7

There are no exercises for this chapter.

### Chapter 8

#### Exercise 1

Make a program that generates a very short story. Get it to take some input of a person, a place and an object -- using `IO.gets/1` and combine all three into a little sentence, output with `IO.puts/1`

What we're looking to output to the screen is a string containing a `person`, a `place` and an `object`. Something like this:

``I suggest it was [Colonel Mustard], in the [Ballroom], with the [Revolver]``

First, we need to collect the pieces:

person = IO.gets("Whodunit? ") place = IO.gets("Where? ") object = IO.gets("With what? ")

We'll need to trim all of these, as when `IO.gets/1` gives us our values it will include a new line at the end:

``person = "Colonel Mustard\n"``

This will lead to our output to look a little... strange:

``````I suggest it was [Colonel Mustard]
, in the [Ballroom]
, with the [Revolver]``````

We can strip it like this:

``````person = String.trim(IO.gets("Whodunit? "))
place = String.trim(IO.gets("Where? "))
object = String.trim(IO.gets?("With what? "))``````

(We'll see a cleaner way of writing this code in Chapter 10's exercise solutions!)

With everything collected, now we can output it:

``IO.puts "I suggest it was [#{person}] in the [#{place}] with the [#{object}]"``

The whole program looks like this:

``````person = String.trim(IO.gets("Whodunit? "))
place = String.trim(IO.gets("Where? "))
object = String.trim(IO.gets("With what? "))

IO.puts "I suggest it was [#{person}] in the [#{place}] with the [#{object}]"``````

If we put this code into a file called `clue.ex` and run it with `elixir clue.ex`, and answer all its prompts, we'll see the output we wished for:

``I suggest it was [Colonel Mustard], in the [Ballroom], with the [Revolver]``

#### Exercise 2

Ponder on what happens when you remove the `IO.puts` from the beginning of Line 3 in `greet.exs` and then run the program with `elixir greet.exs`. Think about how this would be different if you put that code into an `iex` prompt.

If we remove the `IO.puts` line from `greet.exs`, nothing will be output. This is different from the `iex` prompt, which will show us the string, with the `name` and `age` variables put in their right place:

``````iex> "Hello, #{String.trim(name)}! You're #{String.trim(age)}? That's so old!"
"Hello, Ryan! You're 31? That's so old!"``````

This is because `iex` is a Read-Eval-Print Loop, it will read in the code, evaluate it, and print (or output) whatever the code tells it to do. This is different from running the program `greet.exs`, because that only outputs when we tell it to do so. When we remove the `IO.puts` from the beginning of the line, we're telling it to evaluate the string, but not to output it.

### Chapter 9

#### Exercise 1

Use a combination of `Enum.map/2` and `String.replace/3` to replace all the e's in these words with another letter of your choosing: `["a", "very", "fine", "collection", "of", "words", "enunciated"]`

If we wanted to replace the letter "e" in the word "very", we can do that easily with `String.replace/3`. The three arguments are the string we want to change, what in that string we want to replace and what we want to replace it with.

``````iex> String.replace("very", "e", "a")
"vary"``````

To do this with a list of strings, we must employ `Enum.map/2`. The two arguments here are the word list, and then a function to run over each of them:

``````iex> words = ["a", "very", "fine", "collection", "of", "words", "enunciated"]
["a", "very", "fine", "collection", "of", "words", "enunciated"]
iex> Enum.map(words, fn(word) -> String.replace(word, "e", "a") end)
["a", "vary", "fina", "collaction", "of", "words", "anunciatad"]
``````

The words have now all had their "e" letters replaced with "a", which makas tha word list look a littla stranga, don't you rackon?

We can write this a little shorter by using the capture operator:

``````iex> Enum.map(words, &(String.replace(&1, "e", "a")))
["a", "vary", "fina", "collaction", "of", "words", "anunciatad"]
``````

#### Exercise 2

Use `Enum.reduce/2` to multiply these numbers together: `[2, 4, 991, 2543]`

The final example of `Enum.reduce/2` shows us how to add together a list of scores:

``````iex> scores = [74, 79, 89, 32, 79, 70, 32, 69, 76, 73, 88, 73, 82, 31]
[74, 79, 89, 32, 79, 70, 32, 69, 76, 73, 88, 73, 82, 31]
iex> Enum.reduce(scores, fn (score, sum) -> sum + score end)
947``````

We can adapt this to multiply together our new list:

``````iex> numbers = [2, 4, 991, 2543]
[2, 4, 991, 2543]
iex> Enum.reduce(numbers, fn (number, sum) -> sum * number end)
20160904``````

### Chapter 10

Use your newfound knowledge of the pipe operator to re-write your solution to Chapter 8's first exercise.

As a little refresher, here's what our end solution to Chapter 8 looked like:

``````person = String.trim(IO.gets("Whodunit? "))
place = String.trim(IO.gets("Where? "))
object = String.trim(IO.gets("With what? "))

IO.puts "I suggest it was [#{person}] in the [#{place}] with the [#{object}]"``````

We have to read this code from the inside-out -- we read the argument that's passed to `IO.gets/1`, then we need to read `IO.gets/1` itself to understand that, then we read the `String.trim/1` function.

The pipe operator can make this code easier to understand:

``````person = "Whodunit? " |> IO.gets |> String.trim
place = "Where? " |> IO.gets |> String.trim
object = "With what?" |> IO.gets |> String.trim``````

Now our code reads left-to-right, just like a sentence.

For bonus points, we can take out this repetition and move it into a function:

``````question = fn (question) ->
IO.gets("#{question} ") |> String.trim
end

person = question.("Whodunit?")
place = question.("Where?")
object = question.("With what?")

IO.puts "I suggest it was [#{person}] in the [#{place}] with the [#{object}]"``````

When we run this code again, it will work just the same. The function is the same, but the form is now a lot cleaner with the pipe.

### Chapter 11

• Can you make Elixir write a program for itself? Put the following code into a file called `script.ex` with `File.write/2`, writing to a file called `generated.ex`. Here's the code that should go in `script.ex`: `IO.puts "This file was generated from Elixir"`. When we run `elixir script.ex`, we should then be able to run `elixir that-file.ex` and see our output.
• Figure out what happens if you try to delete a file that doesn't exist with `File.rm/1`. Is this what you expected to happen?

### Chapter 12

There are no exercises for this chapter.

### Chapter 13

There are no exercises for this chapter.

### Chapter 14

There are no exercises for this chapter.

### Chapter 15

There are no exercises for this chapter.

### Chapter 16

There are no exercises for this chapter.

### Chapter 17

Add a new route that accepts a date in the format of `1987-12-04` as a parameter. Use your `Person.age` function to return how old this would make the person.

The first step here is to add a new route to `People.Router`, defined in `lib/router.ex`

``````defmodule People.Router do
use Plug.Router

plug(:match)
plug(:dispatch)

get("hello/:name", to: People.Hello)
get("goodbye/:name", to: People.Goodbye)
get("age/:birthday", to: People.Age)

match _ do
send_resp(conn, 404, "there's nothing here")
end
end``````

This route goes to a plug that does not exist, and so we will need to define a module with an `init` and `call` function inside it:

``````defmodule People.Age do
import Plug.Conn

def init(options), do: options

def call(conn, _opts) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "You are #{age} years old!")
end
end``````

Inside the `call` function here, we do not have an `age` variable yet. We'll need to calculate that using the `Person.age/1` function... but that function takes a `Person` struct, and that struct must contain a value for `birthday` so that our calculation works. We need something in this shape:

``%Person{birthday: "1987-12-04"}``

But instead of a hard-coded birthday, we need the value from the parameter. Let's pull that out using pattern matching first:

``````def call(%Plug.Conn{params: %{"birthday" => birthday}} = conn, _opts) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "You are #{age} years old!")
end``````

Now we will have a `birthday` value that we can use to construct a person struct:

``````def call(%Plug.Conn{params: %{"birthday" => birthday}} = conn, _opts) do
age = %People.Person{birthday: birthday} |> People.Person.age

conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "You are #{age} years old!")
end``````

This will get us most of the way there, but if we attempt to run the code now we'll see it blow up:

``````
[timestamp] [error] #PID<0.394.0> running People.Router (connection #PID<0.384.0>, stream id 3) terminated
Server: localhost:4001 (http)
Request: GET /age/1987-12-04
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in Date.diff/2
(elixir 1.10.4) lib/calendar/date.ex:577: Date.diff(~D[2021-02-03], "1987-12-04")
(people 0.1.0) lib/person.ex:12: People.Person.age/1
(people 0.1.0) lib/age.ex:7: People.Age.call/2
(people 0.1.0) lib/plug/router.ex:284: People.Router.dispatch/2
(people 0.1.0) lib/router.ex:1: People.Router.plug_builder_call/2``````

This exception is pointing squarely at our `People.Person.age` function, and at its use of `Date.diff/2`. This function takes two dates, but we've given it a date and a string. So we need to convert the `birthday` variable into a date!

``````def call(%Plug.Conn{params: %{"birthday" => birthday}} = conn, _opts) do
age = %People.Person{birthday: Date.from_iso8601!(birthday)} |> People.Person.age

conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "You are #{age} years old!")
end``````

This will then ensure that when the birthday goes through to `People.Person.age` that it's a date, and we can then compare two dates with `Date.diff/2`. A tricky little step, wasn't that?

It's also worth pointing out here that we use `People.Person` to refer to that module twice, but we could use only `Person` if we aliased the module at the top of `Person.Age`

``````defmodule People.Age do
import Plug.Conn
alias People.Person

def init(options), do: options

def call(%Plug.Conn{params: %{"birthday" => birthday}} = conn, _opts) do
age = %Person{birthday: Date.from_iso8601!(birthday)} |> Person.age

conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "You are #{age} years old!")
end
end``````

This makes our code a little bit longer vertically, for the sake of saving some horizontal length.