SICP Chapter #02 Examples in Erlang
-module(sicp02).
-export([start/0]).
print(X) ->
io:write(X),
io:format("~n").
start() ->
section_2_1_1(),
section_2_1_2(),
section_2_1_3(),
section_2_1_4(),
section_2_2_1(),
section_2_2_2(),
section_2_2_3(),
section_2_2_4(),
section_2_3_1(),
section_2_3_2(),
section_2_3_3(),
section_2_3_4(),
section_2_4_1(),
section_2_4_2(),
section_2_4_3(),
section_2_5_1().
% Functions defined in previous chapters
gcd(A, 0) -> A;
gcd(A, B) -> gcd(B, A rem B).
fib(0) -> 0;
fib(1) -> 1;
fib(N) -> fib(N - 1) + fib(N - 2).
identity(X) -> X.
% 2 Building Abstractions with Data
linear_combination(A, B, X, Y) ->
(A * X) + (B * Y).
mul(A, B) -> A * B.
linear_combination_1(A, B, X, Y) ->
mul(A, X) + mul(B, Y).
% 2.1.1 Introduction to Data Abstraction - Example: Arithmetic Operations for Rational Numbers
section_2_1_1() ->
X = cons(1, 2),
Y = cons(3, 4),
Z = cons(X, Y),
print (car(X)),
print (cdr(X)),
print (car(car(Z))),
print (car(cdr(Z))),
One_Half = make_rat(1, 2),
print_rat(One_Half),
One_Third = make_rat(1, 3),
print_rat(add_rat(One_Half, One_Third)),
print_rat(mul_rat(One_Half, One_Third)),
print_rat(add_rat(One_Third, One_Third)),
print_rat(add_rat_1(One_Third, One_Third)).
cons(X, Y) -> [X, Y].
car([H|_]) -> H.
cdr([_|T]) -> T.
cadr(X) -> car(cdr(X)).
make_rat(N, D) -> [N, D].
numer(X) -> hd(X).
denom(X) -> hd(tl(X)).
add_rat(X, Y) ->
make_rat(numer(X) * denom(Y) + numer(Y) * denom(X), denom(X) * denom(Y)).
sub_rat(X, Y) ->
make_rat(numer(X) * denom(Y) - numer(Y) * denom(X), denom(X) * denom(Y)).
mul_rat(X, Y) ->
make_rat(numer(X) * numer(Y), denom(X) * denom(Y)).
div_rat(X, Y) ->
make_rat(numer(X) * denom(Y), denom(X) * numer(Y)).
equal_rat(X, Y) ->
((numer(X) * denom(Y)) == (numer(Y) * denom(X))).
print_rat(X) ->
io:write(numer(X)),
io:format('/'),
io:write(denom(X)),
io:format("~n").
% reducing to lowest terms in constructor %
make_rat_1(N, D) ->
G = gcd(N, D),
[N div G, D div G].
add_rat_1 (X, Y) ->
make_rat_1((numer(X) * denom(Y)) + (numer(Y) * denom(X)), denom(X) * denom(Y)).
% Exercise 2.1
make_rat_2(N, D) ->
if
((D < 0 andalso N < 0) orelse N < 0) -> [D * -1, N * -1];
true -> [D, N]
end.
% 2.1.2 Introduction to Data Abstraction - Abstraction barriers
section_2_1_2() ->
R = make_rat_3(6, 9),
print (numer_3(R)),
print (denom_3(R)).
% reducing to lowest terms in selectors
make_rat_3(N, D) -> cons(N, D).
numer_3(X) ->
G = gcd(car(X), cadr(X)),
car(X) div G.
denom_3(X) ->
G = gcd(car(X), cadr(X)),
cadr(X) div (G).
% Exercise 2.2
make_point(X, Y) -> [X, Y].
x_point(Point) -> hd(Point).
y_point(Point) -> hd(tl(Point)).
make_segment(Start_Segment, End_Segment) -> [Start_Segment, End_Segment].
start_segment_(Segment) -> hd(Segment).
end_segment_(Segment) -> hd(tl(Segment)).
midpoint_segment(Segment) ->
S = start_segment_(Segment),
E = end_segment_(Segment),
make_point((x_point(S) + x_point(E)) / 2.0, (y_point(S) + y_point(E)) / 2.0).
print_point(P) ->
io:format('('),
io:write(x_point(P)),
io:format(','),
io:write(y_point(P)),
io:format(")~n").
% Exercise 2.3
square(X) -> X * X.
length_segment(Segment) ->
S = start_segment_(Segment),
E = end_segment_(Segment),
math:sqrt(square(x_point(E) - x_point(S)) + square(y_point(E) - y_point(s))).
% Constructors create type tagged using
-record(axy, {anchor, xlen, ylen}).
-record(seg, {start_segment, end_segment}).
make_rectangle(Anchor, Xlen, Ylen) ->
#axy{anchor=Anchor, xlen=Xlen, ylen=Ylen}.
make_rectangle_2(Start_Segment, End_Segment) ->
#seg{start_segment=Start_Segment, end_segment=End_Segment}.
% 'length_rectangle' and 'width_rectangle' act as an abstraction barrier for higher-level
% procedures because 'rectangle' implementation details are buried here, and should the
% implementation change, only these procedures will need to be altered to support the change
length_rectangle(#axy{anchor=Anchor, xlen=Xlen, ylen=Ylen}) -> 0; % Compute length ...
length_rectangle(#seg{start_segment=Start_Segment, end_segment=End_Segment}) -> 0. % Compute length ...
width_rectangle(Rect) ->
% As per 'length_rectangle' except that rectangle width is returned ...
0.
% High-level procedures are quarantined from representation / implementation details
area_rectangle(Rect) ->
length_rectangle(Rect) * width_rectangle(Rect).
perimeter_rectangle(Rect) ->
length_rectangle(Rect) * 2 + width_rectangle(Rect) * 2.
% 2.1.3 Introduction to Data Abstraction - What is meant by data?
section_2_1_3() ->
% Exercise 2.6
Zero = fun (F) -> fun (X) -> X end end.
cons1(X, Y) ->
fun (N) ->
case N of
0 -> X;
1 -> Y;
true -> throw({illformed_expression, "Argument not 0 or 1 -- CONS", N})
end
end.
car1(Z) -> Z(0).
cdr1(Z) -> Z(1).
% Exercise 2.4
cons2(X, Y) ->
fun (M) ->
M(X, Y)
end.
car2(Z) ->
Z(fun (P, Q) -> P end).
cdr2(Z) ->
Z(fun (P, Q) -> Q end).
% Exercise 2.5
cons3(X, Y) ->
math:pow(2, X * math:pow(3, Y)).
car3(Z) ->
case Z rem 2 =:= 0 of
true -> car3((Z div 2) + 1);
false -> 0
end.
cdr3(Z) ->
case Z rem 3 =:= 0 of
true -> cdr3((Z div 3) + 1);
false -> 0
end.
% Exercise 2.6
add1(N) ->
fun (F) ->
fun (X) ->
F((N(F))(X))
end
end.
% 2.1.4 Introduction to Data Abstraction - Extended Exercise: Interval Arithmetic
section_2_1_4() ->
% Exercise 2.9
I = make_interval(5.0, 10.0),
J = make_interval(15.0, 25.0),
print (width(I) + width(J)),
% width of the sum (or difference) of two intervals *is* a function only of the widths of the intervals being added (or subtracted)
print (width(add_interval(I, J))),
print (width(sub_interval(I, J))),
% width of the product (or quotient) of two intervals *is not* a function only of the widths of the intervals being multiplied (or divided)
print (width(mul_interval(I, J))),
print (width(div_interval(I, J))).
make_interval(A, B) -> {A, B}.
lower_bound({A, B}) -> A.
upper_bound({A, B}) -> B.
add_interval(X, Y) ->
make_interval(lower_bound(X) + lower_bound(Y), upper_bound(X) + upper_bound(Y)).
min(X, Y) when X > Y -> Y;
min(X, Y) -> X.
max(X, Y) when X < Y -> Y;
max(X, Y) -> X.
mul_interval(X, Y) ->
P1 = lower_bound(X) * lower_bound(Y),
P2 = lower_bound(X) * upper_bound(Y),
P3 = upper_bound(X) * lower_bound(Y),
P4 = upper_bound(X) * upper_bound(Y),
make_interval(
min(min(P1, P2), min(P3, P4)),
max(max(P1, P2), max(P3, P4))).
div_interval(X, Y) ->
Z = make_interval(1.0 / upper_bound(Y), 1.0 / lower_bound(Y)),
mul_interval(X, Z).
make_center_width(C, W) ->
make_interval(C-W, C+W).
center(I) ->
(lower_bound(I) + upper_bound(I)) / 2.0.
width(I) ->
(upper_bound(I) - lower_bound(I)) / 2.0.
% parallel resistors
par1(R1, R2) ->
div_interval(mul_interval(R1, R2), add_interval(R1, R2)).
par2(R1, R2) ->
One = make_interval(1.0, 1.0),
div_interval(
One,
add_interval(div_interval(One, R1),
div_interval(One, R2))).
% Exercise 2.7
make_interval_(A, B) -> {A, B}.
lower_bound_({A, B}) -> A.
upper_bound_({A, B}) -> B.
% Exercise 2.8
sub_interval(X, Y) ->
make_interval(lower_bound(X) - lower_bound(Y), upper_bound(X) - upper_bound(Y)).
% Exercise 2.10
is_zero_interval(I) ->
(lower_bound(I) =:= 0.0) orelse (upper_bound(I) =:= 0.0).
div_interval_zero_check(X, Y) ->
case is_zero_interval(Y) of
false -> div_interval(X, Y);
true -> throw({divide_by_zero, "Zero interval divisor"})
end.
% Exercise 2.12
make_center_percent(C, P) ->
make_center_width(C, P * C / 100.0).
percent(I) ->
100.0 * width(I) / center(I).
% 2.2.1 Hierarchical Data and the Closure Property - Representing Sequences
section_2_2_1() ->
One_Through_Four = [1,2,3,4],
print (One_Through_Four),
print (hd(One_Through_Four)),
print (hd(tl(One_Through_Four))),
print ([10|One_Through_Four]),
Squares = [1, 4, 9, 16, 25],
print (list_ref(Squares, 3)),
Odds = [1, 3, 5, 7],
print (length_1(Odds)),
print (length_2(Odds)),
print (append(Squares, Odds)),
print (append(Odds, Squares)),
print (scale_list(10, [1, 2, 3, 4, 5])),
print (map(fun abs/1, [-10, 2.5, -11.6, 17])),
print (map(fun (X) -> X*X end, [1, 2, 3, 4])),
print (scale_list_(10, [1, 2, 3, 4, 5])),
% Exercise 2.17
print (last_pair([23, 72, 149, 34])),
% Exercise 2.18
print (reverse_1([1, 4, 9, 16, 25])),
print (reverse_2([1, 4, 9, 16, 25])),
% Exercise 2.19
US_Coins = [50, 25, 10, 5, 1],
print (cc(100, US_Coins)),
UK_Coins = [100, 50, 20, 10, 5, 2, 1, 0.5],
print (cc(100, UK_Coins)),
% Exercise 2.20
print (same_parity([1, 2, 3, 4, 5, 6, 7])),
print (same_parity([2, 3, 4, 5, 6, 7])),
% Exercise 2.21
print (square_list_1([1, 2, 3, 4])),
print (square_list_2([1, 2, 3, 4])),
% Exercise 2.22
print (square_list_3([1, 2, 3, 4])),
print (square_list_4([1, 2, 3, 4])),
print (square_list_5([1, 2, 3, 4])),
% Exercise 2.23
for_each([57, 321, 88], fun (X) -> print(X) end).
list_ref(Items, 0) -> hd(Items);
list_ref(Items, N) -> list_ref(tl(Items), N-1).
length_1([]) -> 0;
length_1(Items) -> 1 + length_1(tl(Items)).
length_2(Items) ->
Length_Iter =
fun (Self, [], Count) -> Count;
(Self, A, Count) -> Self(Self, tl(A), 1+(Count))
end,
Length_Iter(Length_Iter, Items, 0).
append([], List2) -> List2;
append(List1, List2) -> [hd(List1) | append(tl(List1), List2)].
% Mapping over lists
scale_list(Factor, []) -> [];
scale_list(Factor, [H|T]) -> [H * Factor | scale_list(Factor, T)].
map(Proc, []) -> [];
map(Proc, [H|T]) -> [Proc(H) | map(Proc, T)].
scale_list_(Factor, Items) ->
map(fun (X) -> X * Factor end, Items).
% Exercise 2.17
last_pair(Items) ->
Last_Pair_Iter =
fun (Self, [], Prev) -> Prev;
(Self, [H|T], Prev) -> Self(Self, T, [H])
end,
Last_Pair_Iter(Last_Pair_Iter, Items, []).
% Exercise 2.18
reverse_1([]) -> [];
reverse_1([H|T]) -> append(reverse_1(T), [H]).
reverse_2(Items) ->
Reverse_Iter =
fun (Self, [], Accum) -> Accum;
(Self, [H|T], Accum) -> Self(Self, T, [H|Accum])
end,
Reverse_Iter(Reverse_Iter, Items, []).
% Exercise 2.19
no_more([]) -> true;
no_more(Coin_Values) -> false.
except_first_denomination(Coin_Values) -> tl(Coin_Values).
first_denomination(Coin_Values) -> hd(Coin_Values).
cc(0, Coin_Values) -> 1;
cc(Amount, Coin_Values) ->
case Amount < 0 orelse no_more(Coin_Values) of
true -> 0;
false ->
cc(Amount, except_first_denomination(Coin_Values)) +
cc(Amount - (first_denomination(Coin_Values)), Coin_Values)
end.
% Exercise 2.20
filter_1(Predicate, []) -> [];
filter_1(Predicate, [H|T]) ->
case Predicate(H) of
true -> [H|filter_1(Predicate, T)];
false -> filter_1(Predicate, T)
end.
is_odd(N) -> (N rem 2 =:= 1).
is_even(N) -> not(is_odd(N)).
same_parity(Items) ->
Predicate =
case is_odd(hd(Items)) of
true -> fun is_odd/1;
false -> fun is_even/1
end,
filter_1(Predicate, tl(Items)).
% Exercise 2.21
square_list_1([]) -> [];
square_list_1([H|T]) -> [(H*H)|square_list_1(T)].
square_list_2(Items) ->
map(fun (X) -> X*X end, Items).
% Exercise 2.22
square_list_3(Items) ->
Iter =
fun (Self, [], Answer) -> Answer;
(Self, [H|T], Answer) -> Self(Self, T, [square(H)|Answer])
end,
Iter(Iter, Items, []).
square_list_4(Items) ->
Iter =
fun (Self, [], Answer) -> Answer;
(Self, [H|T], Answer) -> Self(Self, T, Answer ++ [square(H)])
end,
Iter(Iter, Items, []).
square_list_5(Items) ->
Iter =
fun (Self, [], Answer) -> Answer;
(Self, [H|T], Answer) -> Self(Self, T, [square(H)|Answer])
end,
lists:reverse(Iter(Iter, Items, [])).
% Exercise 2.23
for_each([], F) -> {};
for_each([H|T], F) ->
F(H),
for_each(T, F).
% 2.2.2 Hierarchical Data and the Closure Property - Hierarchical Structures
section_2_2_2() ->
X = [[1, 2], [3, 4]],
print (length(X)),
print (count_leaves(X)),
print (scale_tree([1, [2, [3, 4], 5], [6, 7]], 10)),
print (scale_tree_2([1, [2, [3, 4], 5], [6, 7]], 10)),
% Exercise 2.24
print ([1, [2, [3, 4]]]),
% Exercise 2.25
print ([1, 3, [5, 7], 9]),
print ([[7]]),
print ([1, [2, [3, [4, [5, [6, 7]]]]]]),
% Exercise 2.26
X1 = [1, 2, 3],
Y1 = [4, 5, 6],
print (append(X1, Y1)),
print ([X1|Y1]),
print ([X1, Y1]),
% Exercise 2.27
print (X),
print (lists:reverse(X)),
print (deep_reverse(X)),
% Exercise 2.28
print (fringe(X)),
print (fringe([X, X])),
% Exercise 2.30
print (square_tree([1, [2, [3, 4], 5], [6, 7]])),
% Exercise 2.31
print (square_tree_2([1, [2, [3, 4], 5], [6, 7]])),
% Exercise 2.32
print (subsets([1, 2, 3])).
count_leaves([]) -> 0;
count_leaves([[H|S]|T]) -> 1 + count_leaves(S) + count_leaves(T);
count_leaves([H|T]) -> 1 + count_leaves(T).
% Mapping over trees
scale_tree([], Factor) -> [];
scale_tree([[H|S]|T], Factor) -> [[H * Factor | scale_tree(S, Factor)] | scale_tree(T, Factor)];
scale_tree([H|T], Factor) -> [H * Factor | scale_tree(T, Factor)].
scale_tree_2(Tree, Factor) ->
map(fun (X) when is_list(X) -> scale_tree_2(X, Factor);
(X) -> X * Factor
end,
Tree).
% Exercise 2.27
deep_reverse([]) -> [];
deep_reverse([H|T]) when is_list(H) -> append(deep_reverse(T), [deep_reverse(H)]);
deep_reverse([H|T]) -> append(deep_reverse(T), [H]).
% Exercise 2.28
fringe([]) -> [];
fringe([H|T]) when is_list(H) -> append(fringe(H), fringe(T));
fringe([H|T]) -> [H|fringe(T)].
% Exercise 2.29
% List-based representation
% a.
make_mobile(Left, Right) -> [Left, Right].
make_branch(Length, Struc) -> [Length, Struc].
left_branch(Mobile) -> hd(Mobile).
right_branch(Mobile) -> hd(tl(Mobile)).
branch_length(Branch) -> hd(Branch).
branch_structure(Branch) -> hd(tl(Branch)).
% Helpers for b. and c.
branch_weight([]) -> 0;
branch_weight(Branch) ->
case is_list(branch_structure(Branch)) of
true -> branch_weight(branch_structure(Branch));
false -> branch_structure(Branch)
end.
total_branch_length([]) -> 0;
total_branch_length(Branch) ->
case is_list(branch_structure(Branch)) of
true -> branch_length(Branch) + total_branch_length(branch_structure(Branch));
false -> branch_length(Branch)
end.
% b.
total_weight(Mobile) ->
branch_weight(left_branch(Mobile)) + branch_weight(right_branch(Mobile)).
% c. [Not as per specification]
is_mobile_balanced(Mobile) ->
Lmwl = total_branch_length(left_branch(Mobile)) * branch_weight(left_branch(Mobile)),
Rmwl = total_branch_length(right_branch(Mobile)) * branch_weight(right_branch(Mobile)),
Lmwl =:= Rmwl.
% Exercise 2.30
square_tree([]) -> [];
square_tree([H|T]) when is_list(H) -> append([square_tree(H)], square_tree(T));
square_tree([H|T]) -> [(H*H)|square_tree(T)].
% Exercise 2.31
tree_map(Proc, []) -> [];
tree_map(Proc, [H|T]) when is_list(H) -> append([tree_map(Proc, H)], tree_map(Proc, T));
tree_map(Proc, [H|T]) -> [Proc(H)|tree_map(Proc, T)].
square_tree_2(Tree) ->
tree_map(fun (X) -> X * X end, Tree).
% Exercise 2.32
subsets([]) -> [[]];
subsets([H|T]) ->
Rest = subsets(T),
append(Rest, map(fun (X) -> [H|X] end, Rest)).
% 2.2.3 Hierarchical Data and the Closure Property - Sequences as Conventional Interfaces
-record(employee, {name, jobtitle, salary}).
section_2_2_3() ->
print (sum_odd_squares([1, 2, 3, 4, 5])),
print (even_fibs(10)),
% Sequence operations
print (map(fun square/1, [1,2,3,4,5])),
print (filter(fun is_odd/1, [1,2,3,4,5])),
print (accumulate(fun erlang:'+'/2, 0, [1,2,3,4,5])),
print (accumulate(fun erlang:'*'/2, 1, [1,2,3,4,5])),
print (accumulate(fun (X,Y) -> [X|Y] end, [], [1,2,3,4,5])),
print (enumerate_interval(2,7)),
print (enumerate_tree([1, [2, [3, 4], 5]])),
print (sum_odd_squares_2([1, 2, 3, 4, 5])),
print (even_fibs_2(10)),
print (list_fib_squares(10)),
print (product_of_squares_of_odd_elements([1,2,3,4,5])),
Recs = [#employee{name="Fred", jobtitle="Programmer", salary=180},
#employee{name="Hank", jobtitle="Programmer", salary=150}],
print (salary_of_highest_paid_programmer(Recs)),
% Nested mappings
N = 10, % book doesn't define N
print (
accumulate(
fun (X,Y) -> [X|Y] end,
[],
map(
fun (I) ->
map(
fun (J) -> [I, J] end,
enumerate_interval(1, I-1)
)
end,
enumerate_interval(1, N)))),
% Exercise 2.38
print (foldr(fun erlang:'/'/2, 1.0, [1.0,2.0,3.0])),
print (foldl(fun erlang:'/'/2, 1.0, [1.0,2.0,3.0])),
print (foldr(fun (X,Y) -> [X|Y] end, [], [1,2,3])),
print (foldl(fun (X,Y) -> [X|Y] end, [], [1,2,3])).
% same as above
% is_odd(N) -> N rem 2 =:= 1.
% is_even(N) -> not(is_odd(N)).
% square(X) -> X * X.
sum_odd_squares([]) -> 0;
sum_odd_squares([[H|S]|T]) -> sum_odd_squares([H|S]) + sum_odd_squares(T);
sum_odd_squares([H|T]) ->
case is_odd(H) of
true -> square(H) + sum_odd_squares(T);
false -> sum_odd_squares(T)
end.
even_fibs(N) ->
Next =
fun (Self, K) ->
case K > N of
true -> [];
false ->
F = fib(K),
case is_even(F) of
true -> [F|Self(Self, K+1)];
false -> Self(Self, K+1)
end
end
end,
Next(Next, 0).
% Sequence operations
filter(Predicate, []) -> [];
filter(Predicate, [H|T]) ->
case Predicate(H) of
true -> [H | filter(Predicate, T)];
false -> filter(Predicate, T)
end.
accumulate(Oper, Initial, []) -> Initial;
accumulate(Oper, Initial, [H|T]) -> Oper(H, accumulate(Oper, Initial, T)).
enumerate_interval(Low, High) when Low > High -> [];
enumerate_interval(Low, High) -> [Low|enumerate_interval(Low+1, High)].
enumerate_tree([]) -> [];
enumerate_tree([[H|S]|T]) -> append(enumerate_tree([H|S]), enumerate_tree(T));
enumerate_tree([H|T]) -> [H|enumerate_tree(T)].
sum_odd_squares_2(Tree) ->
accumulate(fun erlang:'+'/2, 0, map(fun square/1, filter(fun is_odd/1, enumerate_tree(Tree)))).
even_fibs_2(N) ->
accumulate(fun (X,Y) -> [X|Y] end, [], filter(fun is_even/1, map(fun fib/1, enumerate_interval(0, N)))).
list_fib_squares(N) ->
accumulate(fun (X,Y) -> [X|Y] end, [], map(fun square/1, map(fun fib/1, enumerate_interval(0, N)))).
product_of_squares_of_odd_elements(Sequence) ->
accumulate(fun erlang:'*'/2, 1, map(fun square/1, filter(fun is_odd/1, Sequence))).
isProgrammer(#employee{_=_, jobtitle=JobTitle, _=_}) -> JobTitle =:= "Programmer".
salary(#employee{_=_, _=_, salary=Salary}) -> Salary.
salary_of_highest_paid_programmer(Records) ->
accumulate(fun max/2, 0, map(fun salary/1, filter(fun isProgrammer/1, Records))).
% Nested mappings
flatmap(Proc, Seq) ->
accumulate(fun append/2, [], map(Proc, Seq)).
has_no_divisors(N, 1) -> true;
has_no_divisors(N, C) ->
case N rem C =:= 0 of
true -> false;
false -> has_no_divisors(N, C-1)
end.
is_prime(N) -> has_no_divisors(N, N-1).
prime_sum(X, Y) -> is_prime(X + Y).
make_pair_sum(X, Y) -> {X, Y, X+Y}.
prime_sum_pairs(N) ->
map(
fun make_pair_sum/2,
filter(
fun prime_sum/2,
flatmap(
fun (I) ->
map(
fun (J) -> {I, J} end,
enumerate_interval(1, I-1)
)
end,
enumerate_interval(1, N)))).
remove(Item, Sequence) ->
filter(fun (X) -> X =/= Item end, Sequence).
permutations([]) -> [[]];
permutations(S) ->
flatmap(
fun (X) ->
map(
fun (P) -> [X|P] end,
permutations(remove(X, S)))
end,
S).
% Exercise 2.34
% exercise left to reader to define appropriate functions
% horner_eval(X, Coefficient_Sequence) ->
% accumulate(fun (This_Coeff, Higher_Terms) -> ??FILL_THIS_IN?? end, 0, Coefficient_Sequence).
% horner_eval(2, [1,3,0,5,0,1]).
% Exercise 2.36
% exercise left to reader to define appropriate functions
% accumulate_n(Oper, Init, []) -> [];
% accumulate_n(Oper, Init, Seqs) ->
% [accumulate(Oper, Init, ??FILL_THIS_IN??) |
% accumulate_n(Oper, Init, ??FILL_THIS_IN??)].
% accumulate_n(fun erlang:'+'/2, 0, S).
% Exercise 2.38
foldr(Oper, Initial, Sequence) -> accumulate(Oper, Initial, Sequence).
foldl(Oper, Initial, Sequence) ->
Iter =
fun (Self, Result, []) -> Result;
(Self, Result, [H|T]) -> Self(Self, Oper(Result, H), T)
end,
Iter(Iter, Initial, Sequence).
% Exercise 2.42
% exercise left to reader to define appropriate functions
% queens(Board_Size) ->
% Queen_Cols =
% fun (Self, 0) -> [Empty_Board];
% (Self, K) ->
% filter(
% fun (Positions) -> is_safe(K, Positions) end,
% flatmap(
% fun (Rest_Of_Queens) ->
% map(
% fun (New_Row) -> adjoin_position(New_Row, K, Rest_Of_Queens) end,
% enumerate_interval(1, Board_Size))
% end
% Self(Self, (K-1)))
% end,
% Queen_Cols(Board_Size).
% Exercise 2.43 *)
% exercise left to reader to define appropriate functions
% queens(Board_Size) ->
% Queen_Cols =
% fun (Self, 0) -> [Empty_Board];
% (Self, K) ->
% filter(
% fun (Positions) -> is_safe(K, Positions) end,
% flatmap(
% fun (New_Row) ->
% map(
% fun (Rest_Of_Queens) -> adjoin_position(New_Row, K, Rest_Of_Queens) end,
% queen_cols(K-1))
% end,
% enumerate_interval(1, Board_Size)))
% end,
% Queen_Cols(Board_Size).
% 2.2.4 Hierarchical Data and the Closure Property - Example: a picture language
section_2_2_4() ->
A_Frame = make_frame(make_vect(0.0, 0.0), make_vect(1.0, 0.0), make_vect(0.0, 1.0)),
_ = (frame_coord_map(A_Frame))(make_vect(0.0, 0.0)),
_ = origin_frame(A_Frame),
% print (flip_vert(fun wave/1)),
Wave2 = beside(fun wave/1, flip_vert(fun wave/1)),
Wave4 = below(wave2, fun wave/1),
Wave4_ = flipped_pairs(fun wave/1),
% footnote
Flipped_Pairs = square_of_four(fun identity/1, fun flip_vert/1, fun identity/1, fun flip_vert/1).
% these two routines are to be written
draw_line(X, Y) -> {}.
wave(Xframe) -> Xframe.
-record(vect, {x, y}).
make_vect(X, Y) -> #vect{x=X, y=Y}.
xcor_vect(#vect{x=X, _=_}) -> X.
ycor_vect(#vect{_=_, y=Y}) -> Y.
add_vect(V1, V2) -> make_vect(xcor_vect(V1) + xcor_vect(V2), ycor_vect(V1) + ycor_vect(V2)).
sub_vect(V1, V2) -> make_vect(xcor_vect(V1) - xcor_vect(V2), ycor_vect(V1) - ycor_vect(V2)).
scale_vect(S, V) -> make_vect(S * xcor_vect(V), S * ycor_vect(V)).
-record(frame, {origin, edge1, edge2}).
make_frame(Origin, Edge1, Edge2) -> #frame{origin=Origin, edge1=Edge1, edge2=Edge2}.
origin_frame(#frame{origin=Origin, _=_, _=_}) -> Origin.
edge1_frame(#frame{_=_, edge1=Edge1, _=_}) -> Edge1.
edge2_frame(#frame{_=_, _=_, edge2=Edge2}) -> Edge2.
-record(segment, {x, y}).
start_segment(#segment{x=X, _=_}) -> X.
end_segment(#segment{_=_, y=Y}) -> Y.
% Frames
frame_coord_map(Xframe) ->
fun (V) ->
add_vect(
origin_frame(Xframe),
add_vect(scale_vect(xcor_vect(V), edge1_frame(Xframe)),
scale_vect(ycor_vect(V), edge2_frame(Xframe))))
end.
% Painters
foreach(F, []) -> {};
foreach(F, [H|T]) ->
F(H),
foreach(F, T).
segments_painter(Segment_List) ->
fun (Xframe) ->
foreach(
fun (Segment) ->
draw_line(
(frame_coord_map(Xframe))(start_segment(Segment)),
(frame_coord_map(Xframe))(end_segment(Segment)))
end,
Segment_List)
end.
transform_painter(Painter, Origin, Corner1, Corner2) ->
fun (Xframe) ->
M = frame_coord_map(Xframe),
New_Origin = M(Origin),
Painter(
make_frame(
New_Origin,
sub_vect(M(Corner1), New_Origin),
sub_vect(M(Corner2), New_Origin)))
end.
flip_vert(Painter) ->
transform_painter(
Painter,
make_vect(0.0, 1.0),
make_vect(1.0, 1.0),
make_vect(0.0, 0.0)).
flip_horiz(Painter) ->
transform_painter(
Painter,
make_vect(1.0, 0.0),
make_vect(0.0, 0.0),
make_vect(1.0, 1.0)).
shrink_to_upper_right(Painter) ->
transform_painter(
Painter,
make_vect(0.5, 0.5),
make_vect(1.0, 0.5),
make_vect(0.5, 1.0)).
rotate90(Painter) ->
transform_painter(
Painter,
make_vect(1.0, 0.0),
make_vect(1.0, 1.0),
make_vect(0.0, 0.0)).
rotate180(Painter) ->
transform_painter(
Painter,
make_vect(1.0, 1.0),
make_vect(0.0, 1.0),
make_vect(1.0, 0.0)).
squash_inwards(Painter) ->
transform_painter(
Painter,
make_vect(0.0, 0.0),
make_vect(0.65, 0.35),
make_vect(0.35, 0.65)).
beside(Painter1, Painter2) ->
fun (Xframe) ->
Split_Point = make_vect(0.5, 0.0),
Paint_Left =
transform_painter(
Painter1,
make_vect(0.0, 0.0),
Split_Point,
make_vect(0.0, 1.0)),
Paint_Right =
transform_painter(
Painter2,
Split_Point,
make_vect(1.0, 0.0),
make_vect(0.5, 1.0)),
Paint_Left(Xframe),
Paint_Right(Xframe)
end.
below(Painter1, Painter2) ->
fun (Xframe) ->
Split_Point = make_vect(0.0, 0.5),
Paint_Below =
transform_painter(
Painter1,
make_vect(0.0, 0.0),
make_vect(1.0, 0.0),
Split_Point),
Paint_Above =
transform_painter(
Painter2,
Split_Point,
make_vect(1.0, 0.5),
make_vect(0.0, 1.0)),
Paint_Below(Xframe),
Paint_Above(Xframe)
end.
up_split(Painter, 0) -> Painter;
up_split(Painter, N) ->
Smaller = up_split(Painter, N-1),
below(Painter, beside(Smaller, Smaller)).
flipped_pairs(Painter) ->
Painter2 = beside(Painter, flip_vert(Painter)),
below(Painter2, Painter2).
right_split(Painter, 0) -> Painter;
right_split(Painter, N) ->
Smaller = right_split(Painter, N-1),
beside(Painter, below(Smaller, Smaller)).
corner_split(Painter, 0) -> Painter;
corner_split(Painter, N) ->
Up = up_split(Painter, N-1),
Right = right_split(Painter, N-1),
Top_Left = beside(Up, Up),
Bottom_Right = below(Right, Right),
Corner = corner_split(Painter, N-1),
beside(below(Painter, Top_Left), below(Bottom_Right, Corner)).
square_limit(Painter, N) ->
Quarter = corner_split(Painter, N),
Half = beside(flip_horiz(Quarter), Quarter),
below(flip_vert(Half), Half).
% Higher_order operations
square_of_four(Tleft, Tright, Bleft, Bright) ->
fun (Painter) ->
Top = beside(Tleft(Painter), Tright(Painter)),
Bottom = beside(Bleft(Painter), Bright(Painter)),
below(Bottom, Top)
end.
flipped_pairs_(Painter) ->
Combine4 = square_of_four(fun identity/1, fun flip_vert/1, fun identity/1, fun flip_vert/1),
Combine4(Painter).
square_limit_(Painter, N) ->
Combine4 = square_of_four(fun flip_horiz/1, fun identity/1, fun rotate180/1, fun flip_vert/1),
Combine4(corner_split(Painter, N)).
% Exercise 2.45
% exercise left to reader to define appropriate functions
% Right_Split = split(fun beside/2, fun below/2).
% Up_Split = split(fun below/2, fun beside/2)
% Exercise 2.47
make_frame_2(Origin, Edge1, Edge2) -> [Origin, Edge1, Edge2].
make_frame_3(Origin, Edge1, Edge2) -> [Origin, [Edge1, Edge2]].
% 2.3.1 Symbolic Data - Quotation
% To Be Done.
section_2_3_1() ->
io:format("~n").
% 2.3.2 Symbolic Data - Example: Symbolic Differentiation
section_2_3_2() ->
% dx(x + 3) = 1
print (deriv({sum, x, 3}, x)),
% dx(x*y) = y
print (deriv({product, x, y}, x)),
% dx(x*y + x + 3) = y + 1
print (deriv({sum, {sum, {product, x, y}, x}, 3}, x)),
% dx(x + 3) = 1
print (deriv1({sum, x, 3}, x)),
% dx(x*y) = y
print (deriv1({product, x, y}, x)),
% dx(x*y + x + 3) = y + 1
print (deriv1({sum, {sum, {product, x, y}, x}, 3}, x)).
is_same_number(X, Y) ->
is_number(X) andalso is_number(Y) andalso X == Y.
is_variable(X) ->
is_atom(X).
is_same_variable(X, Y) ->
is_variable(X) andalso is_variable(Y) andalso X =:= Y.
is_sum({sum, _, _}) -> true;
is_sum(_) -> false.
is_product({product, _, _}) -> true;
is_product(_) -> false.
make_sum(X, Y) ->
case is_number(X) andalso is_number(Y) of
true -> X + Y;
false -> {sum, X, Y}
end.
make_product(X, Y) ->
case is_number(X) andalso is_number(Y) of
true -> X * Y;
false -> {product, X, Y}
end.
add_end({sum, X, _}) -> X;
add_end(L) -> throw({invalid, "Invalid pattern match", L}).
aug_end({sum, _, Y}) -> Y;
aug_end(L) -> throw({invalid, "Invalid pattern match", L}).
multiplier({product, X, _}) -> X;
multiplier(L) -> throw({invalid, "Invalid pattern match", L}).
multiplicand({product, _, Y}) -> Y;
multiplicand(L) -> throw({invalid, "Invalid pattern match", L}).
deriv(Exp, Var) ->
case is_number(Exp) of
true -> 0;
false ->
case is_variable(Exp) of
true ->
case is_same_variable(Exp, Var) of
true -> 1;
false -> 0
end;
false ->
case is_sum(Exp) of
true ->
make_sum(
deriv(add_end(Exp), Var),
deriv(aug_end(Exp), Var));
false ->
case is_product(Exp) of
true ->
make_sum(
make_product(multiplier(Exp), deriv(multiplicand(Exp), Var)),
make_product(deriv(multiplier(Exp), Var), multiplicand(Exp)));
false -> throw({invalid, "Invalid expression", Exp})
end
end
end
end.
% With simplification
make_sum1(X, Y) ->
if
is_number(X) andalso X == 0 -> Y;
is_number(Y) andalso Y == 0 -> X;
is_number(X) andalso is_number(Y) -> X + Y;
true -> {sum, X, Y}
end.
make_product1(X, Y) ->
if
is_number(X) andalso X == 0 -> 0;
is_number(Y) andalso Y == 0 -> 0;
is_number(X) andalso X == 1 -> Y;
is_number(Y) andalso Y == 1 -> X;
is_number(X) andalso is_number(Y) -> X * Y;
true -> {product, X, Y}
end.
deriv1(Exp, Var) ->
case is_number(Exp) of
true -> 0;
false ->
case is_variable(Exp) of
true ->
case is_same_variable(Exp, Var) of
true -> 1;
false -> 0
end;
false ->
case is_sum(Exp) of
true ->
make_sum1(
deriv1(add_end(Exp), Var),
deriv1(aug_end(Exp), Var));
false ->
case is_product(Exp) of
true ->
make_sum1(
make_product1(multiplier(Exp), deriv1(multiplicand(Exp), Var)),
make_product1(deriv1(multiplier(Exp), Var), multiplicand(Exp)));
false -> throw({invalid, "Invalid expression", Exp})
end
end
end
end.
% 2.3.3 Symbolic Data - Example: Representing Sets
section_2_3_3() ->
print (is_element_of_set2(3, {tree, 2, {tree, 1, {leaf}, {leaf}} , {tree, 3, {leaf}, {leaf}}})),
print (adjoin_set2(3, {tree, 4, {tree, 2, {leaf}, {leaf}}, {tree, 6, {leaf}, {leaf}}})),
% Exercise 2.63
print (tree_to_list1({tree, 4, {tree, 2, {leaf}, {leaf}}, {tree, 6, {leaf}, {leaf}}})),
print (tree_to_list2({tree, 4, {tree, 2, {leaf}, {leaf}}, {tree, 6, {leaf}, {leaf}}})),
% Exercise 2.64
print (list_to_tree([2, 4, 6])).
% unordered
is_element_of_set(X, []) -> false;
is_element_of_set(X, [H|T]) when X =:= H -> true;
is_element_of_set(X, [H|T]) -> is_element_of_set(X, T).
adjoin_set(X, Set) ->
case is_element_of_set(X, Set) of
true -> Set;
false -> [X|Set]
end.
intersection_set([], _) -> [];
intersection_set(_, []) -> [];
intersection_set([H|T], Set2) ->
case is_element_of_set(H, Set2) of
true -> [H|intersection_set(T, Set2)];
false -> intersection_set(T, Set2)
end.
% ordered
is_element_of_set1(_, []) -> false;
is_element_of_set1(X, [H|T]) when X =:= H -> true;
is_element_of_set1(X, [H|T]) when X < H -> false;
is_element_of_set1(X, [H|T]) -> is_element_of_set1(X, T).
intersection_set1([], _) -> [];
intersection_set1(_, []) -> [];
intersection_set1([X|Xs], [Y, Ys]) when X =:= Y -> [X|intersection_set1(Xs, Ys)];
intersection_set1([X|Xs], [Y, Ys]) when X < Y -> intersection_set1(Xs, [Y|Ys]);
intersection_set1([X|Xs], [Y, Ys]) -> intersection_set1([X|Xs], Ys).
is_element_of_set2(X, {leaf}) -> false;
is_element_of_set2(X, {tree, Y, _, _}) when X =:= Y -> true;
is_element_of_set2(X, {tree, Y, Left, _}) when X < Y -> is_element_of_set2(X, Left);
is_element_of_set2(X, {tree, Y, _, Right}) -> is_element_of_set2(X, Right).
adjoin_set2(X, {leaf}) -> {tree, X, {leaf}, {leaf}};
adjoin_set2(X, {tree, Y, Left, Right}) when X =:= Y -> {tree, Y, Left, Right};
adjoin_set2(X, {tree, Y, Left, Right}) when X < Y -> {tree, Y, adjoin_set2(X, Left), Right};
adjoin_set2(X, {tree, Y, Left, Right}) -> {tree, Y, Left, adjoin_set2(X, Right)}.
% Exercise 2.63
tree_to_list1({leaf}) -> [];
tree_to_list1({tree, Y, Left, Right}) ->
append(tree_to_list1(Left), [Y|tree_to_list1(Right)]).
tree_to_list2(Node) ->
Copy_To_List =
fun (Self, {leaf}, L) -> L;
(Self, {tree, X, Left, Right}, L) -> Self(Self, Left, [X|Self(Self, Right, L)])
end,
Copy_To_List(Copy_To_List, Node, []).
% Exercise 2.64
partial_tree(Elts, 0) -> {{leaf}, Elts};
partial_tree(Elts, N) ->
LeftSize = (N-1) div 2,
RightSize = N - (LeftSize + 1),
LeftResult = partial_tree(Elts, LeftSize),
{LeftTree, NonLeftElts} = LeftResult,
ThisEntry = hd(NonLeftElts),
RightResult = partial_tree(tl(NonLeftElts), RightSize),
{RightTree, RemainingElts} = RightResult,
{{tree, ThisEntry, LeftTree, RightTree}, RemainingElts}.
list_to_tree(Elements) ->
{Result, _} = partial_tree(Elements, length(Elements)),
Result.
% information retrieval
lookup(GivenKey, [{information, Key, Name, Age}|_]) when GivenKey =:= Key -> {information, Key, Name, Age};
lookup(GivenKey, [_|T]) -> lookup(GivenKey, T);
lookup(_, L) -> throw({invalid, "Invalid pattern match", L}).
% (*** 2.3.4 Symbolic Data - Example: Huffman Encoding Trees ***)
section_2_3_4() ->
% Exercise 2.67
SampleTree =
make_code_tree(
make_leaf('A', 4),
make_code_tree(
make_leaf('B', 2),
make_code_tree(
make_leaf('D', 1),
make_leaf('C', 1)))),
SampleMessage = [0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0],
print (decode(SampleMessage, SampleTree)).
make_leaf(Symbol, Weight) ->
{leaf, Symbol, Weight}.
is_leaf({leaf, _, _}) -> true;
is_leaf(_) -> false.
symbol_leaf({leaf, Symbol, _}) -> Symbol;
symbol_leaf(Node) -> throw({invalid, "Invalid pattern match", Node}).
weight_leaf({leaf, _, Weight}) -> Weight;
weight_leaf(Node) -> throw({invalid, "Invalid pattern match", Node}).
symbols({leaf, Symbol, _}) -> [Symbol];
symbols({tree, SubSymbols, _, _, _}) -> SubSymbols.
weight({leaf, _, Weight}) -> Weight;
weight({tree, _, Weight, _, _}) -> Weight.
make_code_tree(Left, Right) ->
{tree,
append(symbols(Left), symbols(Right)),
weight(Left) + weight(Right),
Left,
Right}.
left_node({tree, _, _, Left, _}) -> Left;
left_node(Node) -> throw({invalid, "Invalid pattern match", Node}).
right_node({tree, _, _, _, Right}) -> Right;
right_node(Node) -> throw({invalid, "Invalid pattern match", Node}).
choose_node(0, Node) -> left_node(Node);
choose_node(1, Node) -> right_node(Node);
choose_node(N, _) -> throw({invalid, "Invalid pattern match", N}).
% decoding
decode(Bits, Tree) ->
Decode =
fun (Self, [], _) -> [];
(Self, [H|T], CurrentNode) ->
NextNode = choose_node(H, CurrentNode),
case is_leaf(NextNode) of
true -> [symbol_leaf(NextNode) | Self(Self, T, Tree)];
false -> Self(Self, T, NextNode)
end
end,
Decode(Decode, Bits, Tree).
% sets
adjoin_set3(X, Set) ->
case Set of
[] -> [X];
[H|T] ->
case weight(X) < weight(H) of
true -> [X|Set];
false -> [H|adjoin_set3(X, T)]
end
end.
make_leaf_set([{leaf, Symbol, Weight}|Pairs]) -> adjoin_set3(make_leaf(Symbol, Weight), make_leaf_set(Pairs));
make_leaf_set(Node) -> throw({invalid, "Invalid pattern match", Node}).
% Exercise 2.68
% exercise left to reader to define appropriate functions
% encode([], Tree) -> [];
% encode([H|T], Tree) -> append(encode_symbol(H, Tree), encode(T, Tree)).
% 2.4.1 Multiple Representations for Abstract Data - Representations for Complex Numbers
section_2_4_1() ->
Z = {1.0, 2.0},
print (make_from_real_imag(real_part(Z), imag_part(Z))),
print (make_from_mag_ang(magnitude(Z), angle(Z))).
% Same as above
% fun {Square X} X * X end
% Rectangular
real_part_r({Real, _}) -> Real.
imag_part_r({_, Imag}) -> Imag.
magnitude_r(Z) ->
math:sqrt(square(real_part_r(Z)) + square(imag_part_r(Z))).
angle_r(Z) ->
math:atan2(imag_part_r(Z), real_part_r(Z)).
make_from_real_imag_r(X, Y) -> {X, Y}.
make_from_mag_ang_r(R, A) ->
{R * math:cos(A), R * math:sin(A)}.
% polar
magnitude_p({Mag, _}) -> Mag.
angle_p({_, Ang}) -> Ang.
real_part_p(Z) ->
magnitude_p(Z) * math:cos(angle_p(Z)).
imag_part_p(Z) ->
magnitude_p(Z) * math:sin(angle_p(Z)).
make_from_real_imag_p(X, Y) ->
{math:sqrt(square(X) + square(Y)), math:atan2(Y, X)}.
make_from_mag_ang_p(R, A) -> {R, A}.
% using the abstract type
magnitude(Z) -> magnitude_p(Z).
angle(Z) -> angle_p(Z).
real_part(Z) -> real_part_p(Z).
imag_part(Z) -> imag_part_p(Z).
make_from_real_imag(X, Y) -> make_from_real_imag_p(X, Y).
make_from_mag_ang(R, A) -> make_from_mag_ang_p(R, A).
add_complex(Z1, Z2) ->
make_from_real_imag(
real_part(Z1) + real_part(z2),
imag_part(Z1) + imag_part(Z2)).
sub_complex(Z1, Z2) ->
make_from_real_imag(
real_part(Z1) - real_part(Z2),
imag_part(Z1) - imag_part(Z2)).
mul_complex(Z1, Z2) ->
make_from_mag_ang(
magnitude(Z1) * magnitude(Z2),
angle(Z1) + angle(Z2)).
div_complex(Z1, Z2) ->
make_from_mag_ang(
magnitude(Z1) / magnitude(z2),
angle(Z1) - angle(Z2)).
% %%% 2.4.2 Multiple Representations for Abstract Data - Tagged Data %%%
section_2_4_2() ->
print (add_complex_g(make_from_real_imag_g(3.0, 4.0), make_from_real_imag_g(3.0, 4.0))).
attach_tag({TypeTag, Contents}) -> {TypeTag, Contents}.
type_tag({rectangular, _}) -> rectangular;
type_tag({polar, _}) -> polar;
type_tag({A, _}) -> throw({invalid, "Invalid pattern match", A}).
contents({rectangular, X}) -> X;
contents({polar, X}) -> X;
contents({A, _}) -> throw({invalid, "Invalid pattern match", A}).
is_rectangular({rectangular, _}) -> true;
is_rectangular(_) -> false.
is_polar({polar, _}) -> true;
is_polar(_) -> false.
% Rectangular
make_from_real_imag_rectangular(X, Y) ->
{rectangular, {X, Y}}.
make_from_mag_ang_rectangular(R, A) ->
{rectangular, {R*math:cos(A), R*math:sin(A)}}.
real_part_rectangular({rectangular, {X, _}}) -> X.
imag_part_rectangular({rectangular, {_, Y}}) -> Y.
magnitude_rectangular(Z) ->
math:sqrt(square(real_part_rectangular(Z)) +
square(imag_part_rectangular(Z))).
angle_rectangular(Z) ->
math:atan2(imag_part_rectangular(Z), real_part_rectangular(Z)).
% Polar
make_from_real_imag_polar(X, Y) ->
{polar, {math:sqrt(square(X) + square(Y)), math:atan2(Y, X)}}.
make_from_mag_ang_polar(R, A) ->
{polar, {R, A}}.
magnitude_polar({polar, {X, _}}) -> X.
angle_polar({polar, {_, Y}}) -> Y.
real_part_polar(Z) ->
magnitude_polar(Z) * math:cos(angle_polar(Z)).
imag_part_polar(Z) ->
magnitude_polar(Z) * math:sin(angle_polar(Z)).
% Generic selectors
real_part_g({rectangular, Z}) -> real_part_rectangular({rectangular, Z});
real_part_g({polar, Z}) -> real_part_polar({polar, Z});
real_part_g(A) -> throw({invalid, "Invalid pattern match", A}).
imag_part_g({rectangular, Z}) -> imag_part_rectangular({rectangular, Z});
imag_part_g({polar, Z}) -> imag_part_polar({polar, Z});
imag_part_g(A) -> throw({invalid, "Invalid pattern match", A}).
magnitude_g({rectangular, Z}) -> magnitude_rectangular({rectangular, Z});
magnitude_g({polar, Z}) -> magnitude_rectangular({polar, Z});
magnitude_g(A) -> throw({invalid, "Invalid pattern match", A}).
angle_g({rectangular, Z}) -> angle_rectangular({rectangular, Z});
angle_g({polar, Z}) -> angle_polar({polar, Z});
angle_g(A) -> throw({invalid, "Invalid pattern match", A}).
% Constructors for complex numbers
make_from_real_imag_g(X, Y) ->
make_from_real_imag_rectangular(X, Y).
make_from_mag_ang_g(R, A) ->
make_from_mag_ang_polar(R, A).
% same as before
add_complex_g(Z1, Z2) ->
make_from_real_imag_g(
real_part_g(Z1) + real_part_g(Z2),
imag_part_g(Z1) + imag_part_g(Z2)).
sub_complex_g(Z1, Z2) ->
make_from_real_imag_g(
real_part_g(Z1) - real_part_g(Z2),
imag_part_g(Z1) - imag_part_g(Z2)).
mul_complex_g(Z1, Z2) ->
make_from_mag_ang_g(
magnitude_g(Z1) * magnitude_g(Z2),
angle_g(Z1) + angle_g(Z2)).
div_complex_g(Z1, Z2) ->
make_from_mag_ang_g(
magnitude_g(Z1) / magnitude_g(z2),
angle_g(Z1) - angle_g(Z2)).
% 2.4.3 Multiple Representations for Abstract Data - Data-Directed Programming and Additivity
% To Be Done.
section_2_4_3() ->
io:format("~n").
% 2.5.1 Systems with Generic Operations - Generic Arithmetic Operations
% To Be Done.
section_2_5_1() ->
io:format("~n").
|