Collecting all solutions
findall, bagof, setof, aggregate_all and forall.
So far each answer arrives separately. To work with all answers at once, count them, sum them, sort them, you need the all-solutions predicates.
Here is a small employee database:
% emp(Name, Department, Salary)
emp(ann, eng, 100).
emp(bob, eng, 80).
emp(carla, ops, 70).
emp(dave, ops, 70).
emp(erin, sales, 90).findall(Template, Goal, List)
Run Goal to exhaustion; collect a copy of Template for every solution. It never fails, if there are no solutions you get [].
?- findall(Name, emp(Name, _, _), Names)?- findall(Name-Salary, (emp(Name, _, Salary), Salary >= 80), L)?- findall(S, emp(_, _, S), Ss), sum_list(Ss, Total), length(Ss, N)?- findall(X, emp(X, hr, _), L)setof and bagof
bagof is like findall but fails on no solutions and groups by free variables. setof additionally sorts and removes duplicates. A variable in the goal that doesn't appear in the template is "free" and splits the results into groups, one answer per value:
?- bagof(Name, Salary^emp(Name, Dept, Salary), L)?- setof(Salary, N^D^emp(N, D, Salary), L)The Var^Goal syntax says "I don't care about this variable, don't group on it". Without it, bagof/setof would return one answer per distinct value of every free variable. Watch out: even an anonymous _ counts, so bagof(Name, emp(Name, Dept, _), L) would split eng into two groups (salary 100 and salary 80).
aggregate_all/3
For counting and arithmetic summaries there's a shortcut:
?- aggregate_all(count, emp(_, _, _), N)?- aggregate_all(sum(S), emp(_, ops, S), Total)?- aggregate_all(max(S), emp(_, _, S), Max)?- aggregate_all(max(S, Name), emp(Name, _, S), Best)?- aggregate_all(bag(D), emp(_, D, _), Ds), aggregate_all(set(D), emp(_, D, _), Set)forall/2: "for every … holds …"
forall(Cond, Action) succeeds if Action holds for every solution of Cond. With side effects it's also the idiomatic loop:
?- forall(emp(_, _, S), S >= 70)?- forall(emp(_, _, S), S >= 80)?- forall(emp(N, D, S), format("~w works in ~w and earns ~d~n", [N, D, S]))Exercise: Department payroll
The emp/3 facts above are loaded. Write total_salary(Dept, Total) giving the sum of salaries in a department. An unknown department should give a total of 0.