I have a list of cars (auto in german), where the first Variable is the license-plate and the second one the speed:
[auto(eu-ts884, 69), auto(dn-gh184, 64), auto(ac-lj123, 72)].
Now I try to write an average predicate but it fails with the error message:
ERROR: Arguments are not sufficiently instantiated
My code so far:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
Y is S/L,
L > 0,
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S).
sumKilometer([], 0).
sumKilometer([auto(_, X)|Tail], Sum) :-
sumKilometer(Tail, N),
Sum is N + X.
cardinal([], 0).
cardinal([_|Tail], Result) :-
cardinal(Tail, N),
Result is N + 1.
My code is quite equivalent to that post, although I cannot make out my mistake.
Note: sumKilometer
and cardinal
are working fine.
You write:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
Y is S/L,
L > 0,
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S).
The first problem is that when you call durchschnitt([auto(foo,2)],L,Y)
, L
is a free variable. As a result, you cannot calculate Y is S/L
since both S
and L
are unknown here.
You can however use:
durchschnitt([], 0, 0).
durchschnitt([auto(_, X)|Tail], L, Y):-
cardinal([auto(_, X)|Tail], L),
sumKilometer([auto(_, X)|Tail], S),
Y is S/L.
So here you calculate the average after both L
and S
are known. Furthermore you do not unify the list with [auto(_,X)|Tail]
, etc. A simple check like A = [_|_]
is sufficient:
durchschnitt([], 0, 0).
durchschnitt(A, L, Y):-
A = [_|_],
cardinal(A, L),
sumKilometer(A, S),
Y is S/L.
This will also reduce the amount of time spent packing and unpacking.
You can construct a predicate that calculates the three all at the same time (so without looping twice over the list). You can simply use accumulators, like:
durchschnitt(A,L,Y) :-
durchschnitt(A,0,0,L,Y).
Here the second and third element are the running sum and length respectively.
Now for durchschnitt/5
, there are two cases. In the first case we have reached the end of the list, and we thus have to calculate the average and return it, like:
durchschnitt([],S,L,L,Y) :-
(L \= 0
-> Y is S/L
; Y = 0).
So we use an if-then-else to check if the length is something different than 0
(in the case there are no auto
s in the list, we return 0
as average.
In the recursive case, we simple increment the running length and update the running sum, like:
durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
RSN is RS+Si,
L1 is L+1,
durchschnitt(T,RSN,L1,L,Y).
Or putting it together:
durchschnitt(A,L,Y) :-
durchschnitt(A,0,0,L,Y).
durchschnitt([],S,L,L,Y) :-
(L \= 0
-> Y is S/L
; Y = 0).
durchschnitt([auto(_,Si)|T],RS,RL,L,Y) :-
RSN is RS+Si,
L1 is L+1,
durchschnitt(T,RSN,L1,L,Y).