small medium large xlarge

Hi, I’m doing exercises and I find this solution for the Matchsticks problem:

``````defmodule MatchstickFactory do
def boxes(number) do
big = div(number, 50)
medium = div( rem(number, 50), 20)
small = div(number - ((big*50) + (medium*20)), 5)
remaining = rem(number - ((big*50) + (medium*20)), 5)
%{big: big, medium: medium, small: small, remaining_matchsticks: remaining}
end
end
``````

But I think is very ugly and not functional oriented…..

Your solution is not much different from the one in the code download in file code/work_with_functions/answers/exercise_5.ex. The main difference is that the number was updated between each box calculation. Rewriting your code to resemble the supplied answer would look something like this:

``````defmodule MatchstickFactory do
def boxes(number) do
big = div(number, 50)
number = rem(number, 50)
medium = div(number, 20)
number = rem(number, 20)
small = div(number, 5)
number = rem(number, 5)

%{big: big, medium: medium, small: small, remaining_matchsticks: number}
end
end
``````

This does essentially the same as your original code but is easier on the eyes.

I approached the problem a little differently. I am not saying that my approach is better but it is a different way to look at the problem.

I am a big fan of the pipeline operator `|>` and I tend to think in terms of transforming the data I have into the data I want. So the problem is, how do I transform `98` into `%{big: 1, medium: 2, remaining_matchsticks: 3, small: 1}`

My first stab at the solution used a pipeline of anonymous functions and looked like this:

``````defmodule MatchstickFactory do
def boxes(matchsticks) do
{%{}, matchsticks}
|> (fn {acc, ms} -> {Map.put(acc, :big, div(ms, 50)), rem(ms, 50)} end).()
|> (fn {acc, ms} -> {Map.put(acc, :medium, div(ms, 20)), rem(ms, 20)} end).()
|> (fn {acc, ms} -> {Map.put(acc, :small, div(ms, 5)), rem(ms, 5)} end).()
|> (fn {acc, ms} -> Map.put(acc, :remaining_matchsticks, ms) end).()
end
end
``````

I create a 2 element tuple with an empty map and a number of matchsticks. I pipe the tuple to a function that transforms the input tuple into a tuple containing a map with the number of big boxes, and the second element is the number of remaining matchsticks.

`{ %{}, 89 } -> { %{big: 1}, 39 }`

I have similar anonymous functions for medium and small.

The final anonymous function takes the map and adds the : remaining_matchsticks key with whatever the incoming matchstick count is and returns the updated map.

Like you, I think my original solution doesn’t look very good and is hard to follow.

I have 2 problems with my code. The anonymous functions are a little hard to follow and the syntax for invoking an anonymous function in a pipeline is (fn … end).(). The added parenthesis and dot clutter the code somewhat and having a list of anonymous functions gets hard to follow. I would define functions for big, medium, small, and remaining so that the code reads better even though more lines of code need to be written. I would make these functions private as they are particular to my module implementation and probably not useful outside the module.

The second problem is that I dislike constants that get used in multiple places that don’t have a name. Is the 50 in the div expression related to the 50 in the rem expression? In this case they are but that may not always be the case if the value 50 shows up in other places in the code. I like to use module attributes to clarify the use of constants.

After making the modifications, I have the following:

``````defmodule MatchstickFactory do

@size_big 50
@size_medium 20
@size_small 5

def boxes(matchsticks) do
{%{}, matchsticks}
|> big
|> medium
|> small
|> remaining
end

defp big({acc, ms}), do: {Map.put(acc, :big, div(ms, @size_big)), rem(ms, @size_big)}

defp medium({acc, ms}), do: {Map.put(acc, :medium, div(ms, @size_medium)), rem(ms, @size_medium)}

defp small({acc, ms}), do: {Map.put(acc, :small, div(ms, @size_small)), rem(ms, @size_small)}

defp remaining({acc, ms}), do: Map.put(acc, :remaining_matchsticks, ms)
end
``````

This is more code than the original but I think it is easier to read.

Great post Kim Shrier.

I liked you different way of implementing. In Chatper 02, we didn’t reached advanced usage of maps and `|>` operator, it’s nice to see how these techniques can be used in the first exercises. I really liked your suggestions about the module attributes like constants, make total sense. I’ll update the book with it. I came up with a third solution like this:

``````defmodule MatchstickFactory do
@size_big 50
@size_medium 20
@size_small 5

def boxes(matchsticks) do
{big_boxes, remaining} = put_in_boxes(matchsticks, @size_big)
{medium_boxes, remaining} = put_in_boxes(remaining, @size_medium)
{small_boxes, remaining} = put_in_boxes(remaining, @size_small)

%{
big: big_boxes,
medium: medium_boxes,
small: small_boxes,
remaining_matchsticks: remaining
}
end

# I didn't use a private function because we'll see only in chapter 03
def put_in_boxes(matchsticks, box_size) do
{div(matchsticks, box_size), rem(matchsticks, box_size)}
end
end

``````

What do you think?

Thank you Kim….I don’t see the code so I don’t see the solutions to the exercises lol………

Thank you Ulisses for this book I find really clear and different from other.

Ulisses, your solution looks good. Given what you have introduced about Elixir at this point in the book, it is a better solution than what I proposed.

I like all the solutions to this exercise. Ulisses code is the most succinct and readable to me. The solution I came up with is probably getting a little ahead of the material. I often use reduce (probably a little too much) to solve problems like this. I chose to use a module attribute and a keyword list for the box sizes. Then iterate over the boxes and build up a map with the correct number of matches in each box. Since Elixir data is immutable I’m wondering if the re-creation of maps will slow down performance as opposed to building up the map in one go.

``````defmodule MatchstickFactory do
@boxes [big: 50, medium: 20, small: 5]

def matches(number) do
@boxes
|> Enum.reduce({Map.new, number}, &package/2)
|> remaining()
end

defp package({box, size}, {map, number}) do
amount  = div(number, size)
number  = rem(number, size)
{Map.put(map, box, amount), number}
end

defp remaining({map, number}) do
Map.put(map, :remaining_sticks, number)
end

``````

Here is my attempt. What do you think ?

I used pipe operator, and I considered that remaining_matchsticks is the same as a box of size=1. This avoid a specific case for remaining_matchsticks.

``````defmodule MatchstickFactory do

def boxes(num_matchsticks) do
{num_matchsticks, %{}}
|> boxes(:big, 50)
|> boxes(:medium, 20)
|> boxes(:small, 5)
|> boxes(:remaining_matchsticks, 1)
|> elem(1)
end

defp boxes({num_matchsticks, map}, box_type, box_size) do
{rem(num_matchsticks, box_size), Map.put(map, box_type, div(num_matchsticks, box_size))}
end

end
``````

Cheers

``````defmodule MatchstickFactory do
def boxes(number) do
big = div(number, 50)
medium = div(rem(number, 50), 20)
small = div(rem(rem(number, 50), 20), 5)
remaining_match = rem(rem(rem(number, 50), 20), 5)
%{big: big, medium: medium, small: small, remaining_match: remaining_match}
end
end
``````
You must be logged in to comment