Sorting outputs

I’m using: SWI-Prolog version online https://swish. swi-prolog.org/p/NBA.pl

My objetive is to output the teams by order of max points, I can output them but unordered
do I have to make an auxiliary predicate to ordenate the outputs?

My code looks like this:

bestAttack(Points):-
    team(_, Name),
    findall((CA+CB+CC+CD), jogo(_,Name,_,CA,_,CB,_,CC,_,CD,_), PointsAsTeam1List),
    sumlist(PointsAsTeam1List, TotalPointsAsTeam1),
    findall((FA+FB+FC+FD), jogo(_, _, Name,_,FA,_,FB,_,FC,_,FD), PointsAsTeam2List),
    sumlist(PointsAsTeam2List, TotalPointsAsTeam2),
    Points is TotalPointsAsTeam1 + TotalPointsAsTeam2,
    format('\nEquipa com melhor ataque: '),write(Name).
 ```

There are several ways to do this; this example uses pairs.

With a simple refactoring of the code

bestAttack_item(Points-Name):-
    team(_, Name),
    findall((CA+CB+CC+CD), jogo(_,Name,_,CA,_,CB,_,CC,_,CD,_), PointsAsTeam1List),
    sumlist(PointsAsTeam1List, TotalPointsAsTeam1),
    findall((FA+FB+FC+FD), jogo(_, _, Name,_,FA,_,FB,_,FC,_,FD), PointsAsTeam2List),
    sumlist(PointsAsTeam2List, TotalPointsAsTeam2),
    Points is TotalPointsAsTeam1 + TotalPointsAsTeam2.

bestAttack_2(Sorted) :-
    bagof(Points-Name,bestAttack_item(Points-Name),Items),
    keysort(Items,Sorted).

bestAttack_3(Values) :-
    bestAttack_2(Sorted_pairs),
    pairs_values(Sorted_pairs, Values).

bestAttack_4 :-
    bestAttack_3(Values),
    print_term(Values,[]).
?- bestAttack_item(Item).
Item = 9686-bucks ;
Item = 9384-raptors ;
Item = 9445-philadelphia ;
Item = 9216-celtics ;
Item = 8857-pacers .

?- bestAttack_2(Sorted).
Sorted = [8490-grizzlies, 8567-cavaliers, 8575-knicks, 8605-bulls, 8668-heat, 8778-pistons, 8800-magic, 8815-suns, 8857-pacers, 8927-mavericks, 9075-nuggets, 9081-hornets, 9156-spurs, 9161-utah, 9165-lakers, 9204-nets, 9216-celtics, 9223-timberwolves, 9294-hawks, 9341-rockets, 9350-wizards, 9363-sacramento, 9384-raptors, 9387-thunder, 9402-blazers, 9442-clippers, 9445-philadelphia, 9466-pelicans, 9650-warriors, 9686-bucks].

?- bestAttack_3(Teams).
Teams = [grizzlies, cavaliers, knicks, bulls, heat, pistons, magic, suns, pacers, mavericks, nuggets, hornets, spurs, utah, lakers, nets, celtics, timberwolves, hawks, rockets, wizards, sacramento, raptors, thunder, blazers, clippers, philadelphia, pelicans, warriors, bucks].
?- bestAttack_4.
[ grizzlies,
  cavaliers,
  knicks,
  bulls,
  heat,
  pistons,
  magic,
  suns,
  pacers,
  mavericks,
  nuggets,
  hornets,
  spurs,
  utah,
  lakers,
  nets,
  celtics,
  timberwolves,
  hawks,
  rockets,
  wizards,
  sacramento,
  raptors,
  thunder,
  blazers,
  clippers,
  philadelphia,
  pelicans,
  warriors,
  bucks
]
true.
1 Like

You may want to have a look at library(solution_sequences) as well as library(aggregate) if you are dealing with the database and you want aggregated, ordered, distinct, etc. results.

2 Likes

Taking Jan’s advice; never used these before.

bestAttack_item_2(Name,Points):-
    team(_, Name),
    findall((CA+CB+CC+CD), jogo(_,Name,_,CA,_,CB,_,CC,_,CD,_), PointsAsTeam1List),
    sumlist(PointsAsTeam1List, TotalPointsAsTeam1),
    findall((FA+FB+FC+FD), jogo(_, _, Name,_,FA,_,FB,_,FC,_,FD), PointsAsTeam2List),
    sumlist(PointsAsTeam2List, TotalPointsAsTeam2),
    Points is TotalPointsAsTeam1 + TotalPointsAsTeam2.

bestAttack_5(Names) :-
    aggregate_all(
        bag(Name),
        (order_by([desc(Points)], bestAttack_item_2(Name,Points))),
        Names
    ).
?- bestAttack_5(Names).
Names = [bucks, warriors, pelicans, philadelphia, clippers, blazers, thunder, raptors, sacramento, wizards, rockets, hawks, timberwolves, celtics, nets, lakers, utah, spurs, hornets, nuggets, mavericks, pacers, suns, magic, pistons, heat, bulls, knicks, cavaliers, grizzlies].

Notice that the order is different from the accepted answer because it is sorted using desc.

Noticing that there was another use for aggregate_all I refactored it into this.

bestAttack_6(Names) :-
    aggregate_all(
        bag(Name),
        (
            order_by(
                [desc(Total_points)], 
                (
                    team(_, Name),
                    aggregate_all(
                        sum(Points),
                        (
                            (
                                jogo(_,Name,_,A,_,B,_,C,_,D,_)
                            ;
                                jogo(_, _, Name,_,A,_,B,_,C,_,D)
                            ),
                            Points is (A+B+C+D)
                         ),
                        Total_points
                    )  % aggregate_all
                )
            ) % order_by
        ),
        Names
    ).

Yes that is actually one line of Prolog just spaced out for easier comprehension.
The real trick for this variation was the use of ; for reading jogo/11 for home and away games.

?- bestAttack_6(Names).
Names = [bucks, warriors, pelicans, philadelphia, clippers, blazers, thunder, raptors, sacramento, wizards, rockets, hawks, timberwolves, celtics, nets, lakers, utah, spurs, hornets, nuggets, mavericks, pacers, suns, magic, pistons, heat, bulls, knicks, cavaliers, grizzlies].
```
1 Like

This style of coding is what I typically try to fight about SQL, SPARQL, regular expressions, etc.: one monolithic expression that is hard to read, debug or reuse. I tend to call these write only languages: building them is not too hard. You start with the inner part and keep adding stuff around it. Once it doesn’t work and either someone else wrote it or you wrote it some time ago, you are in trouble …

Refactored into smaller expressions.

team_home_game_points(Team,Points) :-
    jogo(_,Team,_,A,_,B,_,C,_,D,_),
    Points is (A+B+C+D).

team_away_game_points(Team,Points) :-
    jogo(_, _, Team,_,A,_,B,_,C,_,D),
    Points is (A+B+C+D).

team_game_points(Team,Points) :-
    team_home_game_points(Team,Points).
team_game_points(Team,Points) :-
    team_away_game_points(Team,Points).

total_points(Team,Total_points) :-
    team(_, Team),  % guard against invalid team name
    aggregate_all(
        sum(Points),
        team_game_points(Team,Points),
        Total_points
    ).

team_points_descending(Names) :-
    aggregate_all(
        bag(Name),
        order_by(
            [desc(Total_points)],
            total_points(Name,Total_points)
        ),
        Names).
test(005,nondet) :-
    Team = nets,
    team_home_game_points(Team,Points),
    assertion( Points == 107 ).

test(006,all(Points == [107,114,120,111,122,107,119,102,125,91,125,97,112,106,125,144,115,106,111,134,126,116,109,123,114,109,122,94,135,106,99,101,116,112,127,113,103,110,121,105,113])) :-
    Team = nets,
    team_home_game_points(Team,Points).

test(007,nondet) :-
    Team = nets,
    team_away_game_points(Team,Points),
    assertion( Points == 100 ).

test(008,all(Points == [100,112,102,115,96,104,112,100,113,115,104,113,88,112,127,96,87,115,109,117,95,105,145,117,104,114,89,125,148,117,88,114,96,98,116,123,111,144,110,133,108])) :-
    Team = nets,
    team_away_game_points(Team,Points).

test(009, all(Points == [107,114,120,111,122,107,119,102,125,91,125,97,112,106,125,144,115,106,111,134,126,116,109,123,114,109,122,94,135,106,99,101,116,112,127,113,103,110,121,105,113,100,112,102,115,96,104,112,100,113,115,104,113,88,112,127,96,87,115,109,117,95,105,145,117,104,114,89,125,148,117,88,114,96,98,116,123,111,144,110,133,108]) ) :-
    Team = nets,
    team_game_points(Team,Points).

test(010) :-
    Name = nets,
    total_points(Name,Total_points),
    assertion( Total_points == 9204 ).

test(011) :-
    team_points_descending(Names),
    assertion( Names == [bucks,warriors,pelicans,philadelphia,clippers,blazers,thunder,raptors,sacramento,wizards,rockets,hawks,timberwolves,celtics,nets,lakers,utah,spurs,hornets,nuggets,mavericks,pacers,suns,magic,pistons,heat,bulls,knicks,cavaliers,grizzlies] ).
?- run_tests(stats:[5,6,7,8,9,10,11]).
% PL-Unit: stats:[5,6,7,8,9,10,11] ....... done
% All 7 tests passed
true.
1 Like