Skip to content

Instantly share code, notes, and snippets.

@javajosh
Created February 27, 2017 04:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javajosh/b3426c3cba755e6c9641b612b3cac1da to your computer and use it in GitHub Desktop.
Save javajosh/b3426c3cba755e6c9641b612b3cac1da to your computer and use it in GitHub Desktop.
Assignment 1
%%%-------------------------------------------------------------------
%%% @author Josh Rehman
%%% @copyright (C) 2017, JavaJosh Enterprises, Inc.
%%% @doc https://www.futurelearn.com/courses/functional-programming-erlang/1/assignments/161825/submission/new
%%%
%%% @end
%%% Created : 26. Feb 2017 6:02 PM
%%%-------------------------------------------------------------------
-module(pattern).
-author("Josh Rehman").
-export([fib/1, fibP/1, area/1, testArea/0, perimeter/1, max/3, enclose/1, bits/1, pits/1, testFib/0, testBits/0, testPits/0, testMax/0, testAll/0]).
% To make sure I understood what Simon Thomson was talking about WRT more complex
% pattern matching, I went ahead and recreated his example here.
fibP(0) ->
{0,1};
fibP(N) ->
{A,B} = fibP(N-1),
{B, A+B}.
fib(N) ->
{A,_} = fibP(N),
A.
%spot check against some known-good values.
%0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
%0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
% Later on you'll see I adopted a different convention for testing.
testFib() ->
(fib(0) == 0) and (fib(3) == 2) and (fib(8)==21) and (fib(12) ==144).
% These functions are simple and didn't require much testing.
% In real code, I'd want to check values more thoroughly.
% An interesting possibility is that area of a triangle is zero,
% when one side is as long as the other two combined.
area({circle, {X,Y}, R}) ->
math:pi()*R*R;
area({rectangle, {X,Y}, W, H}) ->
W*H;
area({triangle, {X,Y}, A, B, C}) -> % Heron's formula
S = perimeter({triangle, {X,Y}, A, B, C})/2,
math:sqrt(S*(S-A)*(S-B)*(S-C)).
testArea() ->
A = math:pi() == area({circle, {0,0}, 1}),
B = 10 == area({rectangle, {0,0}, 5, 2}),
C = 0 == area({triangle, {0,0}, 1, 2, 3}), % this is correct!
A and B and C.
% Pi is the ratio of circumference to diameter, pi = c/d = c/2r. c=2r(pi)
% no testing required here.
perimeter({circle, {X,Y}, R}) ->
math:pi() * 2 * R;
perimeter({rectangle, {X,Y}, W, H}) ->
2*(W + H);
perimeter({triangle, {X,Y}, A, B, C}) ->
A + B + C.
% All of these seemed more like geometry problems than erlang problems!
% The circle was easy: you need a square with sides equal to the diameter, 2*R.
% The rectangle was easiest, as you just need to return itself.
% The triangle case was tricky! I figured the smallest enclosing rectangle would
% base times height, and selected the longest side for the base, and then
% compute height from the base and area, using A=1/2*b*h.
enclose({circle, {X,Y}, R}) ->
{rectangle, {X,Y}, 2*R, 2*R};
enclose({rectangle, {X,Y}, W, H}) ->
{rectangle, {X,Y}, W, H};
enclose({triangle, {X,Y}, A, B, C}) ->
BASE = max(A,B,C),
%area = 1/2 *base * height -> height = 2*area / base
HEIGHT = 2 * area({triangle, {X,Y}, A, B, C}) / BASE,
{rectangle, {X,Y}, BASE, HEIGHT}.
% This is kind of cool: to sort 3 numbers we only need to check for
% ordering in both directions, and then recurse, swapping an edge into the middle.
% hopefully there is a standard function to do this, but it was fun.
max(A,B,C) when (A>=B) and (B>=C) ->
A;
max(A,B,C) when (A=<B) and (B=<C) ->
C;
max(A,B,C) ->
max(B,A,C).
testMax() ->
A = max(1,2,3) == 3,
B = max(3,2,1) == 3,
C = max(4,5,2) == 5,
D = max(1,1,1) == 1,
A and B and C and D.
% Return the sum of bits in the binary representation.
% Recall that dividing by two shifts the binary to the right.
% This is the direct recursive
% NOTE: Erlang was incredibly unhelpful diagnosing integer division problem
% because at first I tried N/2 which actually hung the REPL, repeatedly.
% The break through came when I added the (what I thought was unnecessary)
% guard to the last phrase, and added "when (N rem 2) == 0". This let the
% program terminate quickly with a floating point number, and I realized
% I needed N div 2 instead of N/2.
bits(0) -> 0;
bits(1) -> 1;
bits(N) when (N rem 2) == 1 ->
1 + bits(N-1);
bits(N) ->
bits(N div 2).
testBits() ->
A = bits(0) == 0,
B = bits(1) == 1,
C = bits(3) == 2,
D = bits(4) == 1,
E = bits(64) == 1,
F = bits(65) == 2,
G = bits(13189123) == 7,
A and B and C and D and E and F and G.
% Indirect recursive
pits(N) -> pits(N, 0).
pits(0, ACC) ->
ACC;
pits(N, ACC) when (N rem 2) == 1 ->
pits(N-1, ACC + 1);
pits(N, ACC) ->
pits(N div 2, ACC).
testPits() ->
A = pits(0) == 0,
B = pits(1) == 1,
C = pits(3) == 2,
D = pits(4) == 1,
E = pits(64) == 1,
F = pits(65) == 2,
G = pits(13189123) == 7,
A and B and C and D and E and F and G.
% Which is preferable? In this case I much prefer the direct recursion because
% it's much cleaner - no ACC parameter and no "interface" function with arity 1.
% Also, performance will not be a big issue since the depth of recursion will
% only ever be proportional to the digits, that is log base 2 N.
testAll() ->
A = testFib(),
B = testBits(),
C = testPits(),
D = testMax(),
A and B and C and D.
% Couple of thoughts so far:
% 1. No multi-line comments? Seems like a strange omission.
% 2. It seems like a bad idea to repeat the "shape" pattern all over.
% 3. It does get a little old typing "c(file)." and then "file:function()."
% (But it does inspire shorter file and function names).
% 4. It's kind of exciting having to solve problems with only the barest minimum tools!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment