0
votes

I'm new to Prolog as I'm just starting to learn and write up my own small set of database rules. Using my own .pl file of database rules, I'm having a small problem with a query that I enter in Prolog, using these rules. Below shows my small database of rules:

staff(andy,18235,3).
staff(beth,19874,4).
staff(andy,18235,5).
staff(carl,16789,2).
staff(earl,34567,9).

sum([], 0).
sum([H|T], X) :-
   sum(T, X1),
   X is X1 + H.

getincome(Name, Income) :-
   findall(Income,staff(Name,_,Income),Member),
   sum(Member, Income).

As you can see, I have written a rule that finds the total income for a particular member of staff. This works very fine, as when I input:

?- getincome(andy, X).

The program always returns:

X = 8

As it should do, however whenever I instead input:

?- getincome(andy, 8).

This always returns false, when it should be true.

However when I also input:

?- getincome(andy, 3).

This returns true, due to already being in the database.

I'm just wondering, how could I modify this rule so that this could output true for the correct summation value, entered for any given staff (most particularly Andy), as opposed to the value already in the given database?

Ignore my question above!

Thanks for the help 'false'. I'm also having another issue, this time to do with working out and displaying the sum of the income for each member. I have modified my rules, in order to display this, as follows:

getincome(Name, I) :- staff(Name, _, _ ), findall(Income,staff(Name,_,Income),Member), sum(Member, I).

Whenever I enter the query:

?- getincome(X, Y).

I keep getting duplicate results of staff (most notably Andy, of course), as show below:

X = andy,
Y = 8 ;
X = beth,
Y = 4 ;
X = andy,
Y = 8 ;
X = carl,
Y = 2 ;
X = earl,
Y = 9.

What changes can I make to avoid these duplicates?

2
You are using Income for two different purposes: Within findall/3 and for sum/2. Rename it within findall/3 to I. - false
Thank you! If you can, could you also help me with another question shown above? - sukh_johal95
Note that you don't need sum/2. There's already a sum_list/2 (or sumlist/2) in Prolog. - lurker
I'll answer your question, but you really should open a new one is SO, not keep adding new questions to your post. - lurker

2 Answers

1
votes

library(aggregate) offers a clean interface to solve such kind of problems:

?- aggregate(sum(S), K^staff(E,K,S), I).
E = andy,
I = 8 ;
E = beth,
I = 4 ;
E = carl,
I = 2 ;
E = earl,
I = 9.
0
votes

One way to do this is to use bagof to collect each set of incomes:

person_income(Name, Income) :-
    bagof(I, X^staff(Name,X,I), Incomes),  % Incomes for a given name
    sumlist(Incomes, Income).           % Sum the incomes

With results:

| ?- person_income(Name, Income).

Income = 8
Name = andy ? a

Income = 4
Name = beth

Income = 2
Name = carl

Income = 9
Name = earl

yes
| ?- person_income(andy, Income).

Income = 8

yes
| ?- person_income(Name, 8).

Name = andy ? a

no
| ?-

I named this person_income to emphasize that it's a relation between a person and their income, rather than getincome which is more of an imperative notion, and doesn't really reflect that you can do more with the relation than just "get the income". Also, I'm using SWI Prolog's sumlist/2 here. GNU Prolog has sum_list/2. And as @CappeliC indicates in his answer, SWI Prolog has a handy aggregate predicate for operations like this.