1

Data:

%flight(FID, Start, Destination, Company, Seats).
%------------------------------------------------------
flight(1, 'Paris', 'Berlin', 'Lufthansa', 210).
flight(2, 'Frankfurt', 'Dubai', 'Lufthansa', 400).
flight(3, 'Rome', 'Barcelona', 'Eurowings', 350).

I want to know all companies that, that have flights with over 200 seats. But each company should be returned only once.

I tried:

q1(Company) :- flight(_, _, _, Company, S), S > 200.

But this returns lufthansa twice. I tried:

q1(Company) :- flight(_, _, _, Company, S), S > 200,!.

But this exited after first return of Lufthansa. I think I have to wrap the condition in a sub query:

q1(Company) :- flight(_, _, _, Company, S), q12(S).
q12(S) :- flight(_, _, _, _, S), S > 200,!.

But this exited after the first return, too. Any idea how I can return a company that matches only once using cut?

FID is the primary key and I am only allowed to use . , + < > <= >=

DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601
  • 2
    First, remove the `!/0`: It destroys the intended meaning of your program. Then collect all companies using `setof/3` like this: `?- setof(Company, q1(Company), Cs).` This gives you a sorted list without duplicates. If you want, you can enumerate the companies for example with `?- setof(C, q1(C), Cs), member(C, Cs).` – mat May 12 '16 at 11:09
  • @mat I am not allowed to use setof, just ", . ! \+ < > <= >=" – DarkLeafyGreen May 12 '16 at 11:42
  • 2
    This is a weird requirement. Can you at least use `sort/2`? Otherwise you need to do a weird trick in the spirit of an SQL left join or something similar. –  May 12 '16 at 12:36
  • @Boris can you show me the weird trick? – DarkLeafyGreen May 12 '16 at 12:37
  • 1
    I would have to think about it a bit. You can try to look for inspiration on the SQL tag :) or maybe someone has a ready answer for you. –  May 12 '16 at 12:39

2 Answers2

4

As I said in the comments, one way to do this is to use setof/3 to obtain a sorted list of results without duplicates, for example:

?- setof(C, q1(C), Cs).

This is a recommended way to remove redundant solutions. You can enumerate such solutions for example with:

?- setof(C, q1(C), Cs), member(C, Cs).

Viewer discretion is advised for the remainder...


There are also several ways to solve this subject to brain-damaged conditions that an unskilled instructor may impose on you. For example, here is an inefficient and highly non-idiomatic way to obtain all companies that occur in your database without using setof/3:

companies(Cs) :-
    companies_([], Cs).

companies_(Cs0, Cs) :-
    (   flight(_, _, _, C, _),
        \+ memberchk(C, Cs0) ->
        companies_([C|Cs0], Cs)
    ;   Cs0 = Cs
    ).

I don't have the stomach to carry this further, so I just end with the hint for you: You only need to insert one goal to solve your task. I hope your teacher is happy with this "solution".

mat
  • 40,498
  • 3
  • 51
  • 78
  • 2
    This uses `memberchk/2`, which is probably also not "allowed" :) but see the other answer for an alternative approach. –  May 12 '16 at 13:54
  • 3
    Everything in this version can be easily expressed using only the shown primitives. For example, `memberchk/2` and if-then-else can be expressed with `!/0`. These are all examples of logic *hacking*, not logic *programming*, so I do not want to show even more in this direction. – mat May 12 '16 at 13:58
  • Why `memberchk/2` anyway? – false May 13 '16 at 17:13
  • I can't even... read `member/2` in combination with `(\+)/1`. Better use `if_/3`. – mat May 13 '16 at 19:38
2

The key here are the unique IDs. Your original question is:

Which companies have flights with over 200 seats?

The query is obvious:

?- flight(_,_,_,C,S), S > 200.
C = 'Lufthansa',
S = 210 ;
C = 'Lufthansa',
S = 400 ;
C = 'Eurowings',
S = 350.

Now, because the IDs are unique, there is going to be a flight within the group with the same company and seats > 200 which has the highest (or lowest) ID. So, you could re-formulate your question as:

Which flights have over 200 seats and the highest ID within the group of flights from the same company?

or, to make if a bit more close to the way that we can pose the query in Prolog,

Given a flight with an ID, Company, and Seats, the Seats must be more than 200, and there must be no other flight from the same company with a higher ID.

?- flight(ID,_,_,C,S), S > 200, \+ ( flight(IDX,_,_,C,_), IDX > ID ).
ID = 2,
C = 'Lufthansa',
S = 400 ;
ID = 3,
C = 'Eurowings',
S = 350.

If you put this query in a predicate you can avoid reporting the ID and the actual number of seats.

By the way, this approach is a courtesy of this answer to a somewhat related question (shameless self-promotion). I really can't remember where I got the idea: I am certain I didn't come up with it myself. If anyone can find a good reference here on Stackoverflow or elsewhere please comment.

Community
  • 1
  • 1
  • Thank you that brought me on the right track. BTW I found another solution that seems correct using the cut operator. I put it in my question. – DarkLeafyGreen May 12 '16 at 14:32
  • @artworkadシ Are you sure you need that cut? What happens if you remove it? –  May 12 '16 at 14:36