Problem statement:
You're given a list containing integers and lists of integers. You must remove
from each sublist, the 1st, 2n, 4th, 8th.. etc, element.
My solution
domains
list=integer*
elem=i(integer);l(list)
clist=elem*
predicates
modify(list, list, integer, integer)
exec(clist, clist)
clauses
modify([], [], _, _).
modify([H|T], Mod, I, P):-
P=I,
!,
I1=I+1,
P1=P*2,
modify(T, Mod, I1, P1).
modify([H,T], [H|Mod], I, P):-
I1=I+1,
modify(T, Mod, I1, P).
exec([], []).
exec([i(N)|T], [i(N)|LR]):-
exec(T, LR).
exec([l(L)|T], [l(Mod)|LR]):-
modify(L, Mod, 1, 1).
do():-
exec([i(1),l([1,2,3,4,5,6,7,8,9]),l([1,2,3,4])],X),
write(X).
The problem is that the algorithm works until it removes the 1st and 2nd element from each sublist, but from then on doesn't remove a thing and I'm not sure what I'm doing wrong.
the exec predicate is used to assert whether the current element is an integer or a list of integers, add the integer to the result, or add the modified list to the result.
the modify predicate modifies a given list and should remove all elements on position power of 2.
I wrote the do predicate just for calling it as a goal, to avoid writing the list every time I want to test it.
I think you're doing P1=P*2 too often, then you mismatch successive powers of 2
also, you have a typo here
modify([H,T], [H|Mod], I, P):- ...
should read
modify([H|T], [H|Mod], I, P):- ...
I would write
modify([], [], _).
modify([_|T], Mod, I):-
is_pow2(I), !, % as noted by SQB
I1 is I+1,
modify(T, Mod, I1).
modify([H|T], [H|Mod], I):-
I1 is I+1,
modify(T, Mod, I1).
To keep is_pow2/1 simple, you could do
is_pow2(1).
is_pow2(2).
is_pow2(4).
is_pow2(8).
is_pow2(16).
...
or use your Prolog arithmetic facilities. In SWI-Prolog a simple minded definition could be
is_pow2(N) :-
between(0, 63, L),
N is 1 << L.
Actually, the core of your modify is correct, so it may a problem in exec. The following works for me:
do_modify(X) :-
modify([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], X, 2).
modify(Lin, Lout, Base) :- modify(Lin, Lout, Base, 1, 1).
modify([], [], _, _, _).
modify([_|T], X, Base, N, Power) :-
N = Power,
!,
P1 is Power * Base,
N1 is N + 1,
modify(T, X, Base, N1, P1).
modify([H|T], [H|X], Base, N, Power) :-
N1 is N + 1,
modify(T, X, Base, N1, Power).
Tested it on http://www.compileonline.com/execute_prolog_online.php.
Related
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.
Sum, Length and Average all concurrently
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 autos 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).
I'm working on a Prolog program which should load every nth element of a list into another list. For example:
?- pred([a,b,c,d,e,f,g,h,i,j],3,R) =>
R = [c,f,i]
Where pred is the predicate I'm attempting to implement.
But I honestly don't know how to do it. I know that I need a counter, which represents the current position of my Head, so it's gonna be a /4 predicate summarized into a /3 one later one, like
nth(list,number,result) :- nth(list,number,result,counter) or similiar.
Though, I don't know how to give the head a position number that can reset itself. Because once it hits n (let's say n=3, which is the c in the list), it has to turn back to 1 logically and count again up to 3, give out the element, and so on.
How can I work around these specific problems in my implementation?
An example of how this could be implemented:
nth_pos(L, N, R):-
nth_pos(L, 1, N, [], R).
nth_pos([], I, N, Acc, Acc).
nth_pos([H|T], I, N, Acc, R):-
I =:= N,
append(Acc, [H], Acc2),
I2 is 1,
nth_pos(T, I2, N, Acc2, R).
nth_pos([H|T], I, N, Acc, R):-
I < N,
I2 is I + 1,
nth_pos(T, I2, N, Acc, R).
Test-run:
?- nth_pos([a,b,c,d,e,f,g,h,i,j],3,R).
R = [c, f, i] .
?- nth_pos([a,b,c,d,e,f,g,h,i,j],1,R).
R = [a, b, c, d, e, f, g, h, i|...]
But I honestly don't know how to do it. I know that I need a counter,
which represents the current position of my Head, so it's gonna be a
/4 predicate summarized into a /3 one later one, like
nth(list,number,result) :- nth(list,number,result,counter) or
similiar.
Yes you are on the right track, note that an accumulator is used as well to build up the list so we get pred/5. Hope it helps. Note that this is not the only way to solve it.
I have a problem,
I have a list with numeric elements such as in the example.
I´d like to find all pairs, and count it. (Every Element can only be one part of one pair)
?- num_pairs([4,1,1,1,4],N).
N=1;
Can anyone help me to solve this problem??
You need several things to make it work:
An ability to count the number an item is repeated in a list
An ability to remove all elements matching a value from the list
An ability to conditionally increment a number
Here is how you can count:
count([], _, 0).
count([H|T], H, R) :- count(T, H, RT), R is RT + 1.
count([H|T], X, R) :- H \= X, count(T, X, R).
Deletion can be done with SWI's delete/3 predicate; this is a built predicate.
Adding one conditionally requires two rules - one when the count equals one, and another one for when the count does not equal one.
add_if_count_is_one(H, T, RT, R) :- count(T, H, 1), R is RT + 1.
add_if_count_is_one(H, T, R, R) :- count(T, H, X), X \= 1.
Finally, counting pairs could look like this:
num_pairs([], 0).
num_pairs([H|T], R) :- delete(T, H, TT),
num_pairs(TT, RT),
add_if_count_is_one(H, T, RT, R).
An empty list has no pairs; when an item is counted as part of a pair, its copies are removed from the rest of the list.
Here is this running program on ideone.
This is the question for one of my assignments:
Write repCount(L, X, N) which is true when N is the number of occurrences of X in list L.
Here's my code where I try to tackle the problem recursively:
repCount([], X, N) :-
N is 0.
repCount([H|T], X, N) :-
count([H|T], X, N).
count([], X, 0).
count([H|T], X, N) :-
count(T, X, N1),
X =:= H,
N is N1 + 1.
And it works when I supply a list full of identical numbers like this:
?- repCount([2,2,2], 2, N).
N = 3.
But if I supply a list with at least one different value:
?- repCount([2,2,22], 2, N).
false.
It returns false. I cannot figure out why this happens or how to change it to 'skip' the non-matching value, rather than declare the whole thing false. Any input is appreciated.
count([H|T], X, N):- count(T, X, N1), X=:=H, N is N1 + 1.
here you declare that N should be N1+1 if X is H; however you do not define what should happen if X is not H (basically missing an else clause)
this should work:
count([H|T], X, N):-
count(T, X, N1),
(X=:=H->
N is N1 + 1
; N is N1).
another way would be:
count([H|T], X, N):- count(T, X, N1), X=:=H, N is N1 + 1.
count([H|T], X, N):- X=\=H, count(T, X, N1), N is N1.
but this is inefficient since count(T,X,N1) will be called twice if X is not H. we can fix this by doing the check in the head of the clause:
count([H|T], H, N):- count(T, X, N1), N is N1 + 1.
count([H|T], X, N):- count(T, X, N1), N is N1.
or simply:
count([H|T], H, N):- count(T, X, N1), N is N1 + 1.
count([H|T], X, N1):- X=\=H, count(T, X, N1).
One maybe interesting addition to what #magus wrote: If you only care about the number of elements instead of the elements themselves, you can use findall/3 like this:
list_elem_num(Ls, E, N) :-
findall(., member(E, Ls), Ds),
length(Ds, N).
Preserve logical-purity—with a little help from
meta-predicate tcount/3 and (=)/3!
The goal tcount(=(X),Es,N) reads "there are N items in list Es that are equal to X".
Sample query:
?- tcount(=(X), [a,b,c,a,b,c,a,b,a], N).
( N = 4, X=a
; N = 3, X=b
; N = 2, X=c
; N = 0, dif(X,a), dif(X,b), dif(X,c)
). % terminates universally
But assuming you aren't allowed to 'cheat', if you want to use recursion, you don't need to do the '==' comparison.. you can use Prolog's variable unification to reach the same end:
% Job done all instances
repCount2([], _, 0).
% Head unifies with X/2nd parameter - ie X found
repCount2([H|T], H, N) :-
repCount2(T, H, NewN),
N is NewN + 1.
% We got here, so X not found, recurse around
repCount2([_|T], X, N) :-
repCount2(T, X, N).
In the second predicate, H is mentioned twice, meaning that if the Head of the list is the same as X, then recurse down, then add 1 to the result of the rest of the recursion (which ends in adding 0 - the base case, which is how the accumulator is built).
Almost there...you need to use an accumulator, thus:
repCount(Xs,Y,N) :-
count(Xs,Y,0,N) % the 3rd argument is the accumulator for the count, which we seed as 0
.
count([],_,N,N). % if the list is empty, unify the accumulator with the result
count([X|Xs],Y,T,N) :- % if the list is non-empty,
X == Y , % and the head of the list (X) is the the desired value (Y),
T1 is T+1 , % then increment the count, and
count(Xs,Y,T1,N) % recurse down, passing the incremented accumulator
. %
count([X|Xs],Y,T,N) :- % if the list is non-empty,
X \== Y , % and the head of the list(X) is not the desired value (Y),
count(Xs,Y,T,N) % simply recurse down
. %
The original question didn't say whether there were constraints on which predicates you could use.
If you are allowed to 'cheat' ie. use higher order predicates like 'findall' that recurse for you Vs you doing the recursion yourself, this can be done in a single predicate:
repCount(L, X, N) :-
findall(X, member(X, L), ListOfX),
length(ListOfX, N).
How to sum all odd positioned elements in a list
example [1,2,3,4,5,6,7,8,9] = 25
odd([],0].
odd([Z],Z).
odd([X,Y|T], Sum+1):- odd(T,Sum).
but it return me 1+3+5+7+9.
In prolog you have to use the is operator when you want to evaluate arithmetic expressions. Since you use the + symbol outside of an arithmetic scope it is not interpreted specially. This appears to be homework, so I'll give a simplified example:
add(A, B, C) :- C is A + B.
The code above adds A and B and stores the result in C.
What you construct when you write Sum+1 is a term with functor '+'/2 and arguments Sum and 1.
In Prolog, when you want to calculate a sum, you need to use the predicate is/2.
In your code, you should also add cuts to remove unnecessary choicepoints, and add X to the rest of the sum, not 1:
odd([],0) :- !.
odd([Z],Z) :- !.
odd([X,_|T],Sum):- odd(T,Sum0), Sum is Sum0+X.
Using an accumulator would allow you to make the code tail-recursive...
Get a list with the odd elements, then sum that list:
divide([], [], []).
divide([H|T], [H|L1], L2) :- divide(T, L2, L1).
sum(L, Sum) :- sum(L, 0, Sum).
sum([], Acu, Acu).
sum([H|T], Acu, Acu1) :-
Acu2 is Acu + H,
sum(T, Acu2, Acu1).
sum_odd(L, Sum) :-
divide(L, Odds, _),
sum(Odds, Sum).
:- sum_odd([1,2,5,6,8,9,1], Sum), writeln(Sum).
sum([],0).
sum([H|T],N) :-
sum(T,M), N is H + M.