Question

So let's say I'm trying to find the sum of all factors of 5 below a certain maximum number. I'm doing this recursively, because that seemed easiest. This is my code:

isFactor(X):-
    Y is X mod 5,
    Y = 0.

sumAll(Number, Result):-
    sumAll(Number, 0, Result).

sumAll(Number, RunningTotal, Result):-
    (isFactor(Number) -> 
        NextTotal is RunningTotal + Number; 
        NextTotal is RunningTotal),
    NextNumber is Number - 1,
    (NextNumber > 0 -> 
        mulSum(NextNumber, NextTotal, NextResult); 
        NextResult is RunningTotal),
    number(NextResult) ->                      % this test is so that the interpreter
        write(NextResult), nl;                 % doesn't print out a bunch of extra stuff
        true.                                  % (the internal IDs of each binding of 
                                               % NextResult maybe?) after the answer.

Now, this works (that is, it prints the correct sum), but I am slightly miffed that I can't figure out how to arrange the code so that doing

| ?- sumAll(10, X).

binds X to 10, rather than printing '10' and asserting 'yes' at the end.

My instinct is to somehow rebind Result to NextResult if NextNumber > 0 (line 13) is true, but I suspect that's just years of Python programming trying to assert themselves.

Is there a way of 'returning' the result of a goal all the way up the nested recursions here? Or am I just thinking about this all wrong?

Was it helpful?

Solution

That's awfully complicated for something simple. To sum all elements of a list that are divisible by N, all you need is this tail recursive implementation:

sum_all( Xs , N , Sum ) :-
  sum_all( Xs , N , 0 , Sum )
  .

sum_all( []     , _ , S , S ) .
sum_all( [X|Xs] , N , T , S ) :-
  X mod N =:= 0 ,
  ! ,
  T1 is T+X ,
  sum_all(Xs,N,T1,S)
  .
sum_all( [_|Xs] , N , T , S ) :-
  sum_all(Xs,N,T,S)
  .

The non-tail recursive implementation is a bit simpler but will blow its stack on a long list:

sum_all( []     , _ , 0 ) .
sum_all( [X|Xs] , N , S ) :-
  sum(Xs,N,T) ,
  ( X mod N =:= 0 -> S is T+X ; S is T )
  .

You could even do something like this to decompose the extraction of "interesting" values from the summing of the list:

sum_all(Xs,N,Sum) :-
  findall( X , ( member(X,Xs), X mod N =:= 0 ) , L ) ,
  sum(L,Sum)
  .

sum(L,S) :- sum(L,0,S).

sum( []     , S ,S ) .
sum( [X|Xs] , T ,S ) :- T1 is T+X , sum(Xs,T1,S) .

Once you have that, then you can simply say:

sum_modulo_N_values( Xs , N ) :-
  sum_all(Xs,N,Sum) ,
  writenl( sum = Sum )
  .

Invoke it something like this

sum_modulo_N_values( [1,2,5,6,7,10,11,15,31,30] , 5 ) .

And you'll get the the expected sum = 60 written to the console.

OTHER TIPS

Your code seems more complex than needed, maybe such complexity hides an important fact: in sumAll(Number, RunningTotal, Result):- Result is a singleton. Then there are little chances to get back the computed value.

I would try to get rid of number(NextResult) -> etc.. (btw you usually need parenthesis to get the expected nesting when using if/then/else - that is (C -> T ; F) ), and 'assign' instead to Result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top