Created
February 27, 2017 13:34
-
-
Save 7-fl/04bd91c759a6380e6fad6690405d8efa to your computer and use it in GitHub Desktop.
Week 1: Putting it all together
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-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