G.M
G.M

Reputation: 663

List and List value manipulations in Erlang

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

Answers (2)

user462003
user462003

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

kjw0188
kjw0188

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

Related Questions