Prolog: find and put into the list duplicates - prolog

Good day, guys. Can't figure out, why prolog predicate is putting all duplicates into my new list. F.e. I have to pick all duplicates:
?- duplicates([a, b, a, a, d, d], R).
R = [a, d]
I have wrote this prolog program:
duplicates([], []).
duplicates([First|Rest], NewRest) :-
not(member(First, Rest)),
duplicates(Rest, NewRest).
duplicates([First|Rest], [First|NewRest]) :-
member(First, Rest),
duplicates(Rest, NewRest).
But it returns:
R = [a, a] .
I think I need to put a (!) sign somewhere, but cannot understand, where. Any suggestions?

library(aggregate) allows a compact solution for your problem.
duplicates(L,D) :- findall(K,(aggregate(count,member(K,L),C),C>1),D).
?- duplicates([a, b, a, a, d, d], R).
R = [a, d].
Clearly, it's less immediate to grasp than #Raubsauger' good answer, and not available in every Prolog out there.

Try this:
duplicates([], []).
duplicates([First|Rest], NewRest) :-
\+ member(First, Rest),
duplicates(Rest, NewRest).
duplicates([First|Rest], NewRest) :-
duplicates(Rest, NewRest),
member(First, NewRest).
duplicates([First|Rest], [First|NewRest]) :-
member(First, Rest),
duplicates(Rest, NewRest),
\+ member(First, NewRest).
?- duplicates([a, b, a, a, d, d], R).
R = [a, d] ;
false.
I have choosen a version where you don't need cuts (the !). Also I added another rule: elements in NewRest can appear only once. Note: membership in NewRest can be testet only after unification of all of its entries.

Related

Generate all words of length N and form a list with them in Prolog

Given the letters [a, b, c] generate the list containing all the words of length N, formed out of this letters.
For example:
?- generate(2, L).
should output:
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
At first, this seemed like a pretty simple problem, but I've discovered that none of my implementations work.
This is the second implementation, the one that kind of works.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T).
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, CurrentList, ResultList) :-
maxSolutions(N, R),
length(CurrentList, L),
L =:= R,
append(CurrentList, [], ResultList), !.
generate(N, CurrentList, ResultList) :-
generateAtomicWord(N, NewWord),
\+ member(NewWord, CurrentList),
append(CurrentList, [NewWord], NewList),
generate(N, NewList, ResultList).
generate(N, ResultList) :-
generate(N, [], ResultList).
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
My first implementation is different, but I can't make it work on any case.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T), !.
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, [H]) :- generateAtomicWord(N, H).
generate(N, [H|T]) :-
generate(N, T),
length(T, TailLen),
maxSolutions(N, M),
(TailLen =:= M -> !;
generateAtomicWord(N, H),
\+ member(H, T)).
This one just outputs:
L = [aa]
and when requested for the rest of the solutions it cycles.
The problem must be solved without using predicates such as:
findall, findnsol, bagof, setof, etc...
that find all the solutions.
I've added the tag backtracking because it does resemble a backtracking problem, but I've no idea what a standard implementation might look like in Prolog.
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
That is not an error, that is the Prolog interpreter that displays the list in a shorter way. If you hit w when it shows the output, it will show the full list. For more information see this answer.
That being said, you make it too hard. You can first make a predicate that will unify a variable with all possible atoms:
letter(X) :- member(X, [a, b, c]).
word(0, []).
word(N, [C|W]) :-
N > 0,
N1 is N-1,
letter(C),
word(N1, W).
Now we can generate all possibilities with findall/3 [swi-doc], and use for example maplist/3 [swi-doc] with atomic_list_concat/2 to convert the list to a single atom:
words(N, L) :-
findall(W, word(N, W), Ws),
maplist(atomic_list_concat, Ws, L).
For example:
?- words(0, L).
L = [''].
?- words(1, L).
L = [a, b, c].
?- words(2, L).
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
?- words(3, L).
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...].
We can generate a list of lists ourselves by updating a "difference" list until all possible words are generated:
wordlist(N, L) :-
wordlist(N, [], L, []).
wordlist(0, R, [W|T], T) :-
reverse(R, W),
!.
wordlist(N, C, L, T) :-
N > 0,
N1 is N-1,
wordfold([a,b,c], N1, C, L, T).
wordfold([], _, _, L, L).
wordfold([C|CS], N1, CT, L, T) :-
wordlist(N1, [C|CT], L, L2),
wordfold(CS, N1, CT, L2, T).
For example:
?- wordlist(0, L).
L = [[]].
?- wordlist(1, L).
L = [[a], [b], [c]].
?- wordlist(2, L).
L = [[a, a], [a, b], [a, c], [b, a], [b, b], [b, c], [c, a], [c|...], [...|...]].
You then still need to perform atomic_list_concat on it. I leave that as an exercise.

Prolog: how can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ;?

Prolog: How can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ; ? The program works well as long as I press ; in the command line, but I want to return directly a list of pairs.
comb(1, [H|_], [H]).
comb(N, [H|T], [H|C]) :- N1 is N - 1, N1 > 0, comb(N1, T, C).
comb(N, [_|T], C):- comb(N, T, C).
This is my program. Thank you very much!
You are looking for findall/3.
findall(+Template, :Goal, -Bag)
Create a list of the instantiations Template gets successively on backtracking over Goal and unify the result with Bag. Succeeds with an empty list if Goal has no solutions. findall/3 is equivalent to bagof/3 with all free variables bound with the existential operator (^), except that bagof/3 fails when Goal has no solutions.
Example:
?- findall(X, comb(2, [a,b,c,d], X), Xs).
Xs = [[a, b], [a, c], [a, d], [b, c], [b, d], [c, d]].

nested list in comp

I am trying to modify my list from [a, (a s b), ( a s b s c), a] to [[a], [a, b], [a,b,c], [a]]
I tried sth like that:
:- op(300, xfy, s).
lol([], List).
lol([X|T], List) :-
X \=(A s B),
lol(T, [[X]|List]).
lol([X s P|T], List) :-
lol([P|T], [[X]|List]).
but this makes:
lol([], [[a], [b], [c], [d], [e]|_G4165]
Your base case is not correct as it leaves the tail of the constructed list unbound. It should be:
lol([], []).
The recursive cases can also be fixed as:
lol([X| Xs], [[X]| Tail]) :-
X \= s(_, _),
lol(Xs, Tail).
lol([X s P| Xs], [[X| Ps]| Tail]) :-
lol([P| Xs], [Ps| Tail]).
With these changes we get:
?- lol([a, (a s b), ( a s b s c), a], List).
List = [[a], [a, b], [a, b, c], [a]] .
To avoid spurious choice points you can either add a cut in the first recursive case:
lol([X| Xs], [[X]| Tail]) :-
X \= s(_, _),
!,
lol(Xs, Tail).
Or combine the two recursive cases using an if-then-else control construct (left as an exercise).

Program doesnt work. Prolog

I have some problem whith this code. The func3 was never invoked :
technology(board, saw, table).
technology(wood, sanded, board).
technology(water, grow, tree).
material(table, board, 20).
material(table, tree, 5).
material(wood, water, 100).
equipment(table,saw, cut, 10).
equipment(board, plane, polish, 7).
equipment(tree, watering, growing, 100).
specialization(saw, wood).
specialization(plane, wood).
specialization(watering, forestry).
plan_vypusku(table,10).
potreba_u_zahotovkah1(M, V):-
write(M + V),
nl,
    technology(F, _, M),
material(M, F, C),
Z is V * C,
write(F - Z),
nl.
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
potreba_u_zahotovkah(C, G):-
findall(X, specialization(X, C), XL),
write(XL),
nl,
plan_vypusku(G, S),
func2(XL, S).
Result:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
Help PLS!
I don't know what you're up to, but I have an explanation of the unexpected failure you observed.
The query you made wrote the following lines by side-effect (write/1 and nl/0) and then failed:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
The highlighted line was output by the following highlighted write/1 and nl/0:
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
So above variable ML was bound to [table] when the goal func3(ML, C) was called.
Looking at your definition of func3/2 the reason for failure becomes apparent:
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
The clause head of func3/2 demands that the first argument is a list having exactly two elements. The list [table], however, has exactly one element, not two!
As no more choicepoint are open, the goal potreba_u_zahotovkah(wood,table) fails.

testing in the console

The consult ?- go(c, g). returns false, but states true for ?- go(a, d).. I do not actually understand it, as I have added the proper rules, and for most of them it works.
Here are the statements I'm using:
door(a, b).
door(b, c).
door(c, d).
door(b, e).
door(e, f).
door(e, g).
go(FromRoom, ToRoom):-
door(FromRoom,ToRoom).
go(FromRoom, ToRoom):-
door(ToRoom, FromRoom).
go(FromRoom, ToRoom) :-
door(FromRoom, NextRoom),
go(NextRoom, ToRoom), !.
go(FromRoom, ToRoom):-
door(ToRoom,NextRoom),
go(NextRoom, FromRoom), !.
You need to add accumulator to your predicate to avoid got stuck in inf loop.
door(a, b).
door(b, c).
door(c, d).
door(b, e).
door(e, f).
door(e, g).
go(From, To, T) :-
T=[From|T1],
go(From, To, [To], T1).
go(From, To,T, T) :-
door(From, To),!.
go(From,To,T,T) :-
door(To,From),!.
go(From, To, Acc,T) :-
door(X, Do),!,
\+ member(X, Acc),
go(From, X, [X|Acc],T).
go(From,To,Acc,T) :-
door(X,Z),!,
\+member(Z,Acc),
go(X,To,[X|Acc],T).
You fourth rule should read
go(FromRoom, ToRoom):-
door(ToRoom,NextRoom),
go(FromRoom,NextRoom),!.
because you already swapped the search order in calling door/2, but this leads to looping, as rightly remarked by #whd. Here a version that 'returns' the path
go(Room, Room, Path, Path).
go(FromRoom, ToRoom, SoFar, Path) :-
( door(FromRoom, NextRoom) ; door(NextRoom, FromRoom) ),
\+ member(NextRoom, SoFar),
go(NextRoom, ToRoom, [NextRoom|SoFar], Path).
test:
?- go(c,g,[],P).
P = [g, e, b, c, d] ;
P = [g, e, b] ;
false.
Due to Prolog search order, it 'takes a tour' in d before going back. It's easy to avoid acting on the SoFar parameter at top level call...

Resources