Table of Contents
Today’s problem was to implement a function that can validate and determine the type of a triangle based on side lengths passed in as arguments. For example, (1, 1, 1)
should tell us that this is a valid triangle and of type equilateral
.
Live solution video
This is my daily live stream video solution to this problem.
- Video available here: https://youtu.be/mkeZfue5F8M
- Also streaming on Twitch: https://www.twitch.tv/percygrunwald
Explanation of the solution
Functions to determine whether a triangle is equilateral, isosceles or scalene
It’s fairly straightforward to implement functions to determine whether a triangle is equilateral, isosceles or scalene. All of them are one-liners using simple equality comparison operators and some boolean and/or
.
Equilateral triangles have 3 sides of equal length:
defp is_equilateral?(a, b, c) do
a == b and b == c
end
Isosceles triangles have 2 sides of equal length:
defp is_isosceles?(a, b, c) do
a == b or a == c or b == c
end
Scalene triangles have 3 sides of different lengths:
defp is_scalene?(a, b, c) do
a != b and a != c and b != c
end
Validation: are all side lengths positive?
Part of the validation we needed to implement was to check if all the sides of the triangle are positive - i.e. greater than 0. This is also easy to implement as a one-liner:
defp all_side_lengths_positive?(a, b, c) do
a > 0 and b > 0 and c > 0
end
Validation: does the triangle meet the triangle inequality?
This validation is a little more complex. The triangle inequality states that for all triangles with non-zero area (i.e. other than a line) each side must be less than the sum of the other sides.
This makes sense, because if any side is equal to the sum of the other sides, it means that the triangle is just two lines end to end. If any side is greater than the sum of the other sides, we’re no longer talking about a triangle at all, since two lines end to end can’t be longer than the sum of their lengths.
Initially, I went down a path of trying to implement a function to get the longest side and compare it to the sum of the other 2 sides. While working on this function, Twitch user SegFaultAx
helpfully pointed out in Twitch chat that this function can also be implemented as a combination of inequalities:
defp triangle_meets_triangle_inequality?(a, b, c) do
b + c > a and a + c > b and a + b > c
end
Main function to tie everything together
Now that we have all the functions to validate and categorize the triangle, we just need to tie everything together.
I opted to use a cond
statement since I think it’s the most readable way to express the flow of this function:
@doc """
Return the kind of triangle of a triangle with 'a', 'b' and 'c' as lengths.
"""
@spec kind(number, number, number) :: {:ok, kind} | {:error, String.t()}
def kind(a, b, c) do
cond do
not all_side_lengths_positive?(a, b, c) ->
{:error, "all side lengths must be positive"}
not triangle_meets_triangle_inequality?(a, b, c) ->
{:error, "side lengths violate triangle inequality"}
is_equilateral?(a, b, c) ->
{:ok, :equilateral}
is_isosceles?(a, b, c) ->
{:ok, :isosceles}
is_scalene?(a, b, c) ->
{:ok, :scalene}
true ->
{:error, "Not a valid triangle"}
end
end
Full solution in text form
Here’s the full solution in text form, in case you want to look over the final product:
defmodule Triangle do
@type kind :: :equilateral | :isosceles | :scalene
@doc """
Return the kind of triangle of a triangle with 'a', 'b' and 'c' as lengths.
"""
@spec kind(number, number, number) :: {:ok, kind} | {:error, String.t()}
def kind(a, b, c) do
cond do
not all_side_lengths_positive?(a, b, c) ->
{:error, "all side lengths must be positive"}
not triangle_meets_triangle_inequality?(a, b, c) ->
{:error, "side lengths violate triangle inequality"}
is_equilateral?(a, b, c) ->
{:ok, :equilateral}
is_isosceles?(a, b, c) ->
{:ok, :isosceles}
is_scalene?(a, b, c) ->
{:ok, :scalene}
true ->
{:error, "Not a valid triangle"}
end
end
defp all_side_lengths_positive?(a, b, c) do
a > 0 and b > 0 and c > 0
end
defp triangle_meets_triangle_inequality?(a, b, c) do
b + c > a and a + c > b and a + b > c
end
defp is_equilateral?(a, b, c) do
a == b and b == c
end
defp is_isosceles?(a, b, c) do
a == b or a == c or b == c
end
defp is_scalene?(a, b, c) do
a != b and a != c and b != c
end
end