问题
I run into this beginners problem, and i don't know how to solve this. Here is my code:
worker( w1, d1, 2000 ) .
worker( w2, d1, 2500 ) .
worker( w2, d2, 1000 ) .
worker( w3, d2, 2000 ) .
worker( w4, d2, 4000 ) .
% worker( W, D, S ) means that worker W works in department D and has salary S
department( d1, w2 ) .
department( d2, w4 ) .
% department( D, B ) means that worker B is director of department D(this is not important in this case)
I need to get sum of all salaries form one of department, like this:
?- department_costs( d1 , T ) .
T = 4500;
no
?- department_costs( D, T ) .
D = d1
T = 4500;
D = d2
T = 7000;
no
?- department_costs( d3 , T ) .
no
I tried this:
department_costs( D, T ):- worker( _X, D, T1 ), T is T1.
And I get this:
?- department_costs( o1, T ).
T=2000;
T=2500;
no
now I need to sum T+T for total costs, but I don't know how to do that. I would like to solve this without using findall/setof/bagof.
EDIT:
I tried with findall:
sumL([], 0).
sumL([G|R], S):-
sumL(R, S1),
S is S1 + G.
department_costs( D, T ):-
findall(P, worker( _X, D, P ), R ),
sumL(R, S),
T=S.
It works fine with department_costs( d1, T ), and department_costs( d2, T ), but when I enter department_costs( D, T ). i get this:
department_costs( D, T ).
O=_h159
T=11500
and it should be like this:
?- department_costs( D, T ) .
D = d1
T = 4500;
D = d2
T = 7000;
can someone tell what is the problem now?
回答1:
Wanting to solve this without findall/3
will just result in a poor attempt at recoding said findall/3
. If you really wanna skip findall/3
, represent your data in a different way, for example:
workers([w1-d1-2000, w2-d1-2500, w2-d2-1000, w3-d2-2000, w4-d2-4000]).
departments([d1-w2, d2-w4]).
In this format you will be able to use recursion and list processing techniques to achieve a good result. In the previous one you will have to go with either database manipulation and/or global variables. Not really Prolog-ish.
For your edit, the problem is that you use findall/3
, and that findall/3
will give you all the results for the one variable you're interested in, but will not precise the bindings that led to those results.
Try bagof/3
instead:
bagof(S, W^worker( W, D, S ), R ).
And see this man page (even if it's SWI, those are ISO Prolog predicates anyway) for more information.
回答2:
library(aggregate) is meant to solve exactly such kind of problems. It's worth to study.
department_costs( D, T ) :- aggregate_all(sum(C), worker( _, D, C ), T).
edit
XSB has 'Tabling Aggregate Predicates' that should allow you to solve efficiently your problem.
回答3:
Kind of crude, and retracts every worker fact used, but will do the trick:
department_costs(D,T) :- worker(X,D,T1), retract(worker(X,D,T1)), department_costs(D,T2), T is T1+T2).
department_costs(_,0).
A less destructive alternative would be to pass around a list of the workers used so far, verify any worker about to be used isn't in the list, and add the newly-used worker to the list for the recursive call. (I suppose you could instead just re-assert the retracted fact at the end of the clause, after the recursive call returns, but that feels REALLY hacky.)
回答4:
I haven't tested this but the idea is you accumulate the workers you counted already. I don't really like it though, if I find more than a couple of minutes for this I'm sure you can do it in a more declarative way without the evil cut.
department_sum(S, D) :-
department_sum_agg([], S, D).
department_sum_agg(Workers, S, D) :-
worker(X,D,SX),
\+ member(X, Workers), !,
department_sum_agg([X|Workers], SRest, D),
S is SX + SRest.
department_sum_agg(_, 0, _).
来源:https://stackoverflow.com/questions/12255117/accumulating-while-in-recursion-backtracking