Assignment for university

So, this is the code I have written so far. The good thing is I do get the correct amount calculated, but the output seems quite odd.

When I use trace, I can see that for [1,2] ‘1425’ is calculated at some point, but the output is still X = 200. I thought about using ‘write’ in test_([],A,B) but there I still get too many outputs.

If you know how to fix it or could give me any hints, I would appreciate it once again :slight_smile:

flighttime(1,200).
flighttime(2,1025).
flighttime(3,770).
flighttime(4,140).
flighttime(0,0)

route(1,'FRA','LHR',15:10,18:30,'DE-AAB').
route(2,'LHR','FRA',20:45,13:50,'DE-AAB').
route(3,'FRA','ATL',05:50,17:00,'DE-AAA').
route(4,'TXL','FRA',21:30,23:50,'DE-AAA').


test([],0).

test_([],A,B):- !,write(B).

test([Head|Tail],Sum):-
flighttime(Head,Flighttime),
route(Head,Departure,Arrival,Time1,Time2,Flighttype),
Sum is Flighttime,
test_(Tail,Arrival,Sum).

test_([Head|Tail],Arrival,Sum):-
route(Head,Departure,Arrival2,Time1,Time2,Flighttype),
flighttime(Head,Flighttime) ,

((Departure == Arrival, Sum2 is Sum + Flighttime)
;
Sum2 is Sum + Flighttime + 180),

test_(Tail,Arrival2,Sum2).

Skonnky

Let me try look to at it later.

Here is some code I gleaned from stack overflow [1] to calculate duration of time, given two time in hour:minutes format.

Perhaps you want to use this instead of creating a new flightime table – just calculate it after retrieving the route data.

duration_time(A:B,X:Y, Length):- 
    Hours is X - A,
    Minutes is Y - B,
    Length is Hours *60 + Minutes.

Edit:

Also, it would be good, if you could seperate out testing the predicate from the predicate definition.

Testing is very important but a seperate concern. You want to keep this separate from the program itself.

[1] operators - Calculating Time Difference in Prolog - Stack Overflow

Ok, thank you very much.

I will try to implement the duration_time but I think it is not necessarily needed.
Do you think implementing it would be a better programming style?

I guess by seperating you mean that I shouldn’t call the predicate test. Am I right?

Best regards and a happy weekend

Skonnky

Yes, its better style – because, the table you created to help you identify the flight duration can be readily calculated from the table you got as given by the exercise.

Also, if the instructor then adds additional routes, your program will not work unless you add entries to the table you added to help you solve the problem – with a predicate that calculates it for you, such an additional table entry is not needed.

So your solution works without any changes if the route table is extended – which it would for each new flight plan.

Right, call it by a name that reflects the (relational) knowledge the predicate conveys, such as total_flight travel_time – and then have a separate predicate called test to test the predicate.

I finished implementing duration_time and I corrected the math by using abs(), but I still have no clue when the program should terminate and output the result.

For testing I just called ‘travel_time([1,2],X).’ via console.

route(1,'FRA','LHR',15:10,18:30,'DE-AAB').
route(2,'LHR','FRA',20:45,13:50,'DE-AAB').
route(3,'FRA','ATL',05:50,17:00,'DE-AAA').
route(4,'TXL','FRA',21:30,23:50,'DE-AAA').

duration_time(A:B,X:Y, Flighttime):-
    Hours is abs(X - A),
    Minutes is abs(Y - B),
    Flighttime is Hours *60 + Minutes.


travel_time([],0).
travel_time_([],_,X).


travel_time([Head|Tail],Sum):-
      route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
      Sum is Flighttime,
      travel_time_(Tail,Arrival,Sum).

travel_time_([Head|Tail],Arrival,Sum):-
      route(Head,Departure,Arrival2,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2, Flighttime),
      ((Departure == Arrival, Sum2 is Sum + Flighttime)
      ;
      Sum2 is Sum + Flighttime + 180),
      travel_time_(Tail,Arrival2,Sum2).

Have a look at this code, and in particular the order of Sum is Flighttime, which is before travel_time_(Tail, Sum).

What you are actually doing here is to first set the value of Sum to flgithtime (ie. Sum is bound to Flighttime), and then call duration_time with that bound value.

This means that Sum was turned into an input argument for the subsequent travel_time_/3 call …

What you actually want is to get, say, a Sum_2 as output, and then add it to Flighttime to get the final Sum.

Can you review that.

Dan

p.s. Recall that in Prolog a variable passed to a predicate call can be input or output – depending on whether its already bound or not – in this case, since its bound you can’t return a new sum – what Prolog does is it binds Sum with the value – and that bound sum is used everywhere in the call.

So, if in the call you try to assign sum, such as so Sum is Flighttime + 30 – then in prolog this becomes a test of equality., since sum is already bound to a value, and flight time, at that time, bound to a value.

This I do understand. I don’t understand where I should put a Sum_2. Do you mean instead of travel_time_(Tail,Arrival,Sum) I should use travel_time_(Tail,Arrival,Sum_2)?
Or do you mean I should introduce another predicate?

How about somethign like this …

travel_time([Head|Tail],Sum):-
      route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
      travel_time_(Tail,Arrival,Sum_1),
      Sum is Flighttime + Sum_1.

Now Sum_1 is unbound when travel_time_/2 is called – hence is able to serve as output variable. Once bound, after the travel_time_/2 call come back, it is added to Sum.

This probably can be rearranged so the recursive call is in the end, to make use of tail recurion opttimization but as a first cut solution, its fine.

There is also a different operation to is that can handle unbound arguments that are going to be bound later (so called lazily), such as so:

travel_time([Head|Tail],Sum):-
      route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
     Sum #= Flighttime + Sum1, 
      travel_time_(Tail,Arrival,Sum_1).

Here the calculation of Sum is deferred, until Sum_1 gets bound, hence after the travel_time_/2 call returns. Like this you get to keep the sequence as before, while the #= operator does some deferred calculation “magic” for you.

@j4n_bur53 – I guess tail call optimization would still apply when the #= operator is used – or do attributed_variables – which, I understand is the mechanism used here to delay computation – interfering with TCO.

Okay thanks for the help. The math is indeed wrong again.

I dont see any other way to use the exact flighttime other than calculating it by hand.

I accidentally used false departure and arrival times.

If I use

travel_time([Head|Tail],Sum):-
  route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
  duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
  Sum #= Flighttime + Sum1,
  total_flight(Tail,Arrival,Sum_1).

It fails in the first call for ‘‘travel_time([1,2],X).’’

Any advice?

Thanks for catching this –

As mentioned, I adapted it, but only tested it briefly.

@skonnky, perhaps best, for the purpose of getting the first step correct, is to replace in the given table the times, with duration – as i’ve done early in this thread.

Something like this:

route(1, fla, lhr, 10).
route(2, fco, fra, 20).
route(3, fra, atl, 30).
route(4, fra, fra, 40).

Dan

It says that this line is expecting an operator.

Sum #= Flighttime + Sum1,

@grossdan you are right. I will handle the duration problem later. I accidentally used the data from last year. For now I have data and correct durations to work with.

I suggest you skip the #= operator for now, i just mentioned it as a general knowledge,

If you do want to use it, i think you need to include one of the constraint solving libraries, from where it come from.

https://www.swi-prolog.org/pldoc/man?section=clpfd

Dan

Okay, many thanks. I will try to read some of the pages.

To get back to my problem, I still dont understand how I can output the endresult.

route(1,'FRA','LHR',15:10,18:30,'DE-AAB').
route(2,'LHR','FRA',20:45,23:50,'DE-AAB').
route(3,'FRA','ATL',05:50,17:00,'DE-AAA').
route(4,'TXL','FRA',21:30,23:50,'DE-AAA').

duration_time(A:B,X:Y,Flighttime):-   % erst checken, welches größer ist
    Time1 is A*60+B,
    Time2 is X*60+Y,
    Flighttime is Time2-Time1.


travel_time([],0).
travel_time_([],_,X).

%backup solution

travel_time([Head|Tail],Sum):-
      route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
      Sum is Flighttime,
      travel_time_(Tail,Arrival,Sum).

travel_time_([Head|Tail],Arrival,Sum):-
      route(Head,Departure,Arrival2,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2, Flighttime),
      ((Departure == Arrival, Sum2 is Sum + Flighttime)
      ;
      Sum2 is Sum + Flighttime + 180),
      travel_time_(Tail,Arrival2,Sum2).

Is it possible for you to give me any more hints?^^

I think in this second clause you are still using Sum as input variable while it should be an output variable

So back to the version before #=, you would want to have:

travel_time([Head|Tail],Sum):-
      route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),  
      travel_time_(Tail,Arrival,Sum_0),
     Sum is Flighttime + Sum_0.

and then the second predicate travel_time_/3 should also have the recursive call before the summation.

The second predicate essentially aims to read as follows:

travel_time of the rest route, is the travel time of the first leg, plus the travel time of the rest. (which reads quite like the first predicate – however, there is more …)

While its also true that the travel time of the first leg is the duration of the first leg, if the previous arrival is the same as the current departure, otherwise, its duration + 180.

First I am a little bit embarrassed but I don’t know what you mean by first leg.

I do think my 2nd predicate is doing what you are saying it should do. Atleast to my understanding.

If I understood your mentioning correctly, the following Sum2 should be an output variable.

travel_time_([Head|Tail],Arrival,Sum):-
      route(Head,Departure,Arrival2,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
      duration_time(Hours1:Minutes1,Hours2:Minutes2, Flighttime),
      ((Departure == Arrival, Sum2 is Sum + Flighttime)
      ;
      Sum2 is Sum + Flighttime + 180),
      travel_time_(Tail,Arrival2,Sum2).

My apologies – i was thinking about the legs of the journey … so one route i called one leg – of the journey.

This Sum is a variable used to output the result.

This Sum2 is supposed to hold the output of the recursive call, which is then used to calculate the total Sum that is then outputted by this predicate.

So in the end you want to have an expression that sets Sum within travel_time_ as so:

travel_time_([Head|Tail], Arrival, Sum) :- 
...

Sum is Sum2 + Flightime. 

and

Sum is Sum2+Flighttime + 180


So where exactly do you think my code is wrong?

travel_time([Head|Tail],Sum):-
route(Head,Departure,Arrival,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
duration_time(Hours1:Minutes1,Hours2:Minutes2,Flighttime),
travel_time_(Tail,Arrival,Sum_0),
Sum is Flighttime + Sum_0.

travel_time_([Head|Tail],Arrival,Sum):-
route(Head,Departure,Arrival2,Hours1:Minutes1,Hours2:Minutes2,Flighttype),
duration_time(Hours1:Minutes1,Hours2:Minutes2, Flighttime),
travel_time_(Tail,Arrival2,Sum2),
((Departure == Arrival, Sum is Sum2 + Flighttime)
;
Sum is Sum2 + Flighttime + 180).

I used the example travel_time([1,3],X). (?? was in this case my travel_time predicate and aaa my travel_time_ predicate).

?- ??([1,3],X).
Call: (10) ??([1, 3], _15880) ? creep
Call: (11) route(1, _16432, _16356, _16360:_16362, _16366:_16368, _16440) ? creep
Exit: (11) route(1, ‘FRA’, ‘LHR’, 15:10, 18:30, ‘DE-AAB’) ? creep
Call: (11) duration_time(15:10, 18:30, _16478) ? creep
Call: (12) _16528 is 1560+10 ? creep
Exit: (12) 910 is 15
60+10 ? creep
Call: (12) _16638 is 1860+30 ? creep
Exit: (12) 1110 is 18
60+30 ? creep
Call: (12) _16478 is 1110-910 ? creep
Exit: (12) 200 is 1110-910 ? creep
Exit: (11) duration_time(15:10, 18:30, 200) ? creep
Call: (11) aaa([3], ‘LHR’, _16898) ? creep
Call: (12) route(3, _16948, _16950, _16954:_16956, _16960:_16962, _17034) ? creep
Exit: (12) route(3, ‘FRA’, ‘ATL’, 5:50, 17:0, ‘DE-AAA’) ? creep
Call: (12) duration_time(5:50, 17:0, _17072) ? creep
Call: (13) _17122 is 560+50 ? creep
Exit: (13) 350 is 5
60+50 ? creep
Call: (13) _17232 is 1760+0 ? creep
Exit: (13) 1020 is 17
60+0 ? creep
Call: (13) _17072 is 1020-350 ? creep
Exit: (13) 670 is 1020-350 ? creep
Exit: (12) duration_time(5:50, 17:0, 670) ? creep
Call: (12) aaa([], ‘ATL’, _17492) ? creep
Exit: (12) aaa([], ‘ATL’, _17492) ? creep
Call: (12) ‘FRA’==‘LHR’ ? creep
Fail: (12) ‘FRA’==‘LHR’ ? creep
Redo: (11) aaa([3], ‘LHR’, _16898) ? creep
Call: (12) _16898 is _17492+670+180 ? creep
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR: [12] _18370 is _18382+670+180
ERROR: [11] aaa([3],‘LHR’,_18412) at c:/users/blabl/desktop/aufgabe1.pl:97
ERROR: [10] ??([1,3],_18444) at c:/users/blabl/desktop/aufgabe1.pl:94
ERROR: [9] toplevel_call(user:user: …) at c:/program files/swipl/boot/toplevel.pl:1113
Exception: (12) _16898 is _17492+670+180 ?
creep
Exception: (11) aaa([3], ‘LHR’, _16898) ?
creep
Exception: (10) ??([1, 3], _15880) ?
creep

Can you tell me how and where to instantiate them correctly?

In the posted code i am missing a baseline condition for the recursion – that ends the recursive calls.

Something like this:

travel_time_([],_Arrival,0).

Adding your baseline condition I retrieved the right result.

X = 1050 for [1,3]

Any chance you can give me your paypal link?:slight_smile:

Is it okay for you if I post another task?