Reputation: 663
I am learning some Erlang and doing exercises from the book, so i got stuck on one of them. It`s better if i quote the whole problem and then explain what i have done so far: "A positive number is happy if by repeated application of the procedure below the number 1 is reached. 1. Square each of the digits of the number 2. Compute the sum of all the squares For example, if you start with 19:
1 * 1 + 9 * 9 = 1 + 81 = 82
8 * 8 + 2 * 2 = 64 + 4 = 68
6 * 6 + 8 * 8 = 36 + 64 = 100
1 * 1 + 0 * 0 + 0 * 0 = 1 + 0 + 0 = 1
(i.e. 19 is a happy number) How do you know when a number is not happy? In fact, every unhappy number will eventually reach the cycle 4, 16, 37, 58, 89, 145, 42, 20, 4, … thus it is sufficient to look for any number in that cycle (say 4), and conclude that the original number is unhappy. Write the functions happy/1, and all_happy/2, which returns whether a number is happy or not (true or false) and all happy numbers between N and M respectively. (Hint: use the functions digitize and sum). Examples:
happy(28) → true
happy(15) → false
happy(5, 25) → [7, 10, 13, 19, 23]"
So, I have created a digitizer/1, which given a positive number N returns a list of the digits in that number:
digitize(N) -> digitize1(N, []).
digitize1(N, Acc) when N > 0 -> digitize1(N div 10, [N rem 10| Acc]);
digitize1(N, Acc) when N == 0 -> Acc.
, and sum/1:
sum(N) when N > 0 -> N + sum(N-1);
sum(0) -> 0.
So for the happy numbers what i have done so far is this:
happy(N) -> happy1(digitize(N), []).
happy1([], Acc) -> (Acc);
happy1([Head|Tail], Acc1) -> happy1(Tail, [Head * Head|Acc1]).
It squares the elements of the list, but i cannot come up with idea of how to sum them and do it again recursively until it reaches 1 or 4. Any help or ideas? And for the second part(all_happy/2), in my non-competent opinion i should use list comprehension, but again, I`m not quite sure how to implement it. Thanks for your time.
Upvotes: 1
Views: 1146
Reputation: 43
Another solution that implements via tail recursion :
digitize(N) -> digitize1(N, [],N).
digitize1(N, Acc,M) when N > 0 -> digitize1(N div 10, [N rem 10| Acc],M);
digitize1(N, Acc,M) when N == 0 -> sum_digits(Acc,0,M).
sum_digits([],Acc,N) when Acc == 1 -> {happy,N,Acc};
sum_digits([], Acc,N) when Acc == 4 -> {unhappy,N,Acc};
sum_digits([],Acc,N) -> {digitize,Acc}, digitize1(Acc,[],N);
sum_digits([H|T],Acc,N)-> sum_digits(T,H*H+Acc,N).
to use:
1> c('test.erl').
test.erl:15: Warning: a term is constructed, but never used
{ok,test}
2> c('test.erl').
test.erl:15: Warning: a term is constructed, but never used
{ok,test}
3> c('test.erl').
test.erl:15: Warning: a term is constructed, but never used
{ok,test}
4> test:digitize(55).
{unhappy,55,4}
5> test:digitize(19).
{happy,19,1}
6> test:digitize(5).
{unhappy,5,4}
7> test:digitize(20).
{unhappy,20,4}
8> test:digitize(21).
{unhappy,21,4}
Upvotes: 0
Reputation: 3685
One thing that I noticed is your happy1 loop can just calculate the sum directly, you don't need to make a list and then add them:
calculate([], Total)->
Total;
calculate([First | Rest], Total) ->
calculate(Rest, Total + (First * First)).
To the main point of your question, you can use pattern matching to detect whether or not you've reached an unhappy number or if you've reached 1.
I have a working implementation, but I'm guessing you'd like to figure out the details for yourself. Let me know if you want me to post it.
Here's my solution:
-module(happy).
-export([happy/1]).
happy(1) ->
happy;
happy(4) ->
not_happy;
happy(Num) ->
io:format("Current loop: ~p~n", [Num]),
Digits = digitize(Num),
happy(calculate(Digits, 0)).
digitize(N) -> digitize1(N, []).
digitize1(N, Acc) when N > 0 -> digitize1(N div 10, [N rem 10| Acc]);
digitize1(N, Acc) when N == 0 -> Acc.
calculate([], Total)->
Total;
calculate([First | Rest], Total) ->
calculate(Rest, Total + (First * First)).
Output:
3> happy:happy(55).
Current loop: 55
Current loop: 50
Current loop: 25
Current loop: 29
Current loop: 85
Current loop: 89
Current loop: 145
Current loop: 42
Current loop: 20
not_happy
4> happy:happy(4).
not_happy
5> happy:happy(19).
Current loop: 19
Current loop: 82
Current loop: 68
Current loop: 100
happy
6> happy:happy(20).
Current loop: 20
not_happy
7> happy:happy(21).
Current loop: 21
Current loop: 5
Current loop: 25
Current loop: 29
Current loop: 85
Current loop: 89
Current loop: 145
Current loop: 42
Current loop: 20
not_happy
If you're interested in how to use a list comprehension, here's the main clause which skips the calculate method and uses the lists:sum
function with the build list:
happy(Num) ->
io:format("Current loop: ~p~n", [Num]),
Digits = [ X * X || X <- digitize(Num)],
happy(lists:sum(Digits)).
Upvotes: 2