Skip to content

Instantly share code, notes, and snippets.

@NeilMadden
Created February 25, 2017 14:11
Show Gist options
  • Save NeilMadden/5dc2994e645d3f8e9503c0ad640adbaa to your computer and use it in GitHub Desktop.
Save NeilMadden/5dc2994e645d3f8e9503c0ad640adbaa to your computer and use it in GitHub Desktop.
Neil Madden Erlang MOOC assignment 1
% Functional Programming in Erlang MOOC
% Week 1 assignment submission - Neil Madden
-module(ex).
-export([perimeter/1,area/1,enclose/1,bits/1]).
% Type definitions! Found out Erlang supports these...
-type point() :: {number(), number()}.
-type circle() :: {circle, point(), number()}.
-type rectangle() :: {rectangle, point(), number(), number()}.
-type triangle() :: {triangle, point(), point(), point()}.
-type shape() :: circle() | rectangle() | triangle().
% area/1 -- calculates the area of a shape
-spec area(shape()) -> number().
area({circle, _, R}) ->
math:pi() * math:pow(R,2);
area({rectangle, _, W, H}) ->
W*H;
area({triangle, {X0,Y0}, {X1,Y1}, {X2,Y2}}) ->
% Area of an arbitrary triangle given vertices, from
% http://www.mathopenref.com/coordtrianglearea.html
% Had to look this up as it's been too long since I did GCSE maths!
abs((X0*(Y1-Y2) + X1*(Y2-Y0) + X2*(Y0-Y1)) / 2.0).
% perimeter/1 -- calculates the perimeter of a shape
-spec perimeter(shape()) -> number().
perimeter({rectangle,_,H,W}) ->
2 * (H + W);
perimeter({circle,_,R}) ->
2 * math:pi() * R;
perimeter({triangle,P1,P2,P3}) ->
distance(P1, P2) + distance(P2,P3) + distance(P1,P3).
% enclose/1 -- calculates the bounding box of a shape
-spec enclose(shape()) -> rectangle().
enclose({rectangle,P,W,H}) ->
{rectangle,P,W,H}; % Probably a simpler way to define this, maybe an is_rect macro?
enclose({circle, {X,Y}, R}) ->
% Subtract radius from X and Y coords of centre point to get bottom-left, then the width
% and height are just twice the radius.
{rectangle, {X-R,Y-R}, 2*R, 2*R};
enclose({triangle, {X0,Y0}, {X1,Y1}, {X2,Y2}}) ->
% Use minThree/3 previously defined (and maxThree/3) to determine least and greatest
% bound coordinates of the three points.
Left = minThree(X0, X1, X2),
Right = maxThree(X0, X1, X2),
Bottom = minThree(Y0, Y1, Y2),
Top = maxThree(Y0, Y1, Y2),
{rectangle, {Left,Bottom}, Right-Left, Top-Bottom}.
% minThree/3 -- returns the minimum of 3 numbers
-spec minThree(number(), number(), number()) -> number().
minThree(X, Y, Z) ->
min(X, min(Y, Z)).
% maxThree/3 -- returns the maximum of 3 numbers
-spec maxThree(number(), number(), number()) -> number().
maxThree(X, Y, Z) ->
max(X, max(Y, Z)).
% distance/2 -- calculates the straight-line distance between two points
-spec distance(point(), point()) -> number().
distance({X0,Y0}, {X1,Y1}) ->
% Pythagoras...
math:sqrt(math:pow(X1-X0,2) + math:pow(Y1-Y0,2)).
% bits/1 -- returns the sum of the bits in the binary representation of the given integer
-spec bits(non_neg_integer()) -> integer().
bits(N) -> bits_iter(N, 0). % Call tail-recursive helper
% bits_iter/2 -- tail-recursive helper for calculating bits/1.
-spec bits_iter(non_neg_integer(), non_neg_integer()) -> non_neg_integer().
bits_iter(0, A) -> A;
bits_iter(1, A) -> 1 + A;
bits_iter(N, A) when N > 1 ->
bits_iter(N div 2, A + N rem 2).
% Direct recursive version of bits would look like:
% bits(0) -> 0;
% bits(1) -> 1;
% bits(N) when N > 1 -> N rem 2 + bits(N rem 2).
%
% The tail-recursive/iterative version is better because it runs in constant space.
@NeilMadden
Copy link
Author

I have interpreted the rectangle {rectangle, Point, Width, Height} with the point being the bottom-left coordinate. Re-watching the videos I see it was supposed to be the centre point, so my solution is different due to that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment