Skip to content

Instantly share code, notes, and snippets.

@7-fl
Created February 27, 2017 13:34
Show Gist options
  • Save 7-fl/04bd91c759a6380e6fad6690405d8efa to your computer and use it in GitHub Desktop.
Save 7-fl/04bd91c759a6380e6fad6690405d8efa to your computer and use it in GitHub Desktop.
Week 1: Putting it all together
-module(ex).
-compile(export_all). %I'm a lazy programmer, what can I say?!
all_tests() ->
perimeter_tests(),
enclose_tests(),
bits_tests(),
tbits_tests().
perimeter_tests() ->
12 = perimeter({square, 3}),
14 = perimeter({rectangle, 3, 4}),
12 = perimeter({triangle, 3, 4, 5}),
% For a circle, I need to check the
% equality of floats:
Result = round(6.2831 * 1000), %Convert correct answer to an integer,
%preserving a few decimal places.
Result = round( %Similarly convert perimeter result to an integer,
%preserving a few decimal places.
perimeter({circle, 1}) * 1000
),
all_tests_passed.
perimeter({circle, R}) ->
2 * math:pi() * R;
perimeter({square, S}) ->
4 * S;
perimeter({rectangle, H, W}) ->
(2 * H) + (2 * W);
perimeter({triangle, A, B, C}) when A+B > C, %Require that the lengths
B+C > A, %be able to form a triangle
C+A > B ->
A + B + C.
%--------------------------
enclose_tests() ->
TargetRect = {rectangle, _H=4, _W=4},
TargetRect = enclose({circle, _R=2} ),
TargetRect = enclose({square, _S=4} ),
TargetRect = enclose({rectangle, _H=4, _W=4} ),
{rectangle, 2.4, 5} = enclose({triangle, _A=3, _B=4, _Hyp=5}),
all_tests_passed.
enclose({circle, R}) ->
{rectangle, 2*R, 2*R};
enclose({square, S}) ->
{rectangle, S, S};
enclose(Shape) when element(1, Shape) =:= rectangle -> %A change of pace!
Shape;
enclose({triangle, A, B, Hyp}) when A+B > Hyp, %Require that the lengths
B+Hyp > A, %be able to form a triangle
Hyp+A > B ->
%Formula for calculating the area of any triangle:
S = (A+B+Hyp)/2,
Area = math:sqrt( S*(S-A)*(S-B)*(S-Hyp) ),
% Enclosing rectangle???
% After some trial and error drawing triangles, it looks like the height
% from the base=hypotenuse can be used as the short side of a bounding
% rectangle, and the hypotenuse will be the long side of the bounding rectangle.
% Therefore, we need to calculate the height of a triangle--using the
% hypontenuse as the base in the formula:
% Area = 1/2 * Base * Height
% We have the Area from above, so rearranging we get:
% Height = (2 * Area)/Base
% Substituting Hyp for Base gives:
% Height = (2*Area)/Hyp
%
Height = (2 * Area)/Hyp,
{rectangle, Height, Hyp}.
%-----------------
bits_tests() ->
%From the problem spec:
3 = bits(7),
1 = bits(8),
% Some more rigorous tests:
1 = bits(2#1),
2 = bits(2#11),
3 = bits(2#111),
4 = bits(2#1111),
5 = bits(2#101010101),
2 = bits(2#1100000000),
all_tests_passed.
% In the bits() solution, I use band (binary and).
% band returns 0 when beyond the end of a number, which is nice.
bits(N) when N>0 ->
%Power = N, % Later, will use math:pow(2, Power).
%Optimizing a bit(still not great):
%Power = (N div 2) + 1,
%Ah hah!
BitLength = length(hd(io_lib:format("~.2.0B", [N]))), % N=9 => ["1001"] => hd() =>
% "1001" => length() => 4
Power = BitLength-1, %If the bit length is 4, e.g. for 2#1001,
bits(N, Power). %the highest power of 2 in that number is 3.
bits(N, 0) ->
N band 1;
bits(N, Power) ->
PowerOfTwo = round(math:pow(2, Power)), % If Power=2 => 2#100, if Power=3 => 2#1000.
BitOnOrOff = (N band PowerOfTwo)/PowerOfTwo, % band returns 0 or a power of 2, so
% convert the power of 2 to 1 by
% dividing by the power of 2.
round(BitOnOrOff) + bits(N, Power-1). % round() converts 0.0 to 0 and 1.0 to 1
%-----------
tbits_tests() ->
% From the problem spec:
3 = tbits(7),
1 = tbits(8),
% Some more rigorous tests:
1 = tbits(2#1),
2 = tbits(2#11),
3 = tbits(2#111),
4 = tbits(2#1111),
5 = tbits(2#101010101),
2 = tbits(2#1100000000),
all_tests_passed.
tbits(N) when N>0 ->
% Power = (N div 2) + 1, % Instead of starting with N, I tried to optimize a bit.
% Optimized Power exactly with this hack:
BitLength = length(hd(io_lib:format("~.2.0B", [N]))), %io_lib:format is like sprintf(),
Power = BitLength - 1, %except it returns a jumble of
BitSum = 0, %nested lists, hence the need for hd().
tbits(N, Power, BitSum).
tbits(N, 0, BitSum) ->
(N band 1) + BitSum;
tbits(N, Power, BitSum) ->
PowerOfTwo = round(math:pow(2, Power)), % If Power=2 => 2#100, if Power=3 => 2#1000.
BitOnOrOff = (N band PowerOfTwo)/PowerOfTwo, % band returns 0 or a power of 2, so
% convert the power of 2 to 1 by
% dividing by the power of 2.
tbits(N, Power-1, BitSum+round(BitOnOrOff)). %round() converts 0.0 to 0 and 1.0 to 1
% Q: Which do you think is better? Why?
%
% A: tbits() is better for Great Erlang Good!
% Tail recursive functions won't blast through memory when
% they need to loop tens of thousands of times.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment