Print elements of a list but handle last element differently - prolog

I have written a predicate that prints out each element in the list except the last. The last element should be handled differently; it should print LAST! instead. This is what I have.
write_data([]).
write_data([X]) :-
!, write('LAST!'), nl.
write_data([X | Rest]) :-
write(x), nl,
write_data(Rest).
Is there a better way? Is there a way to do this without the cut?

You can avoid the cut, by performing unification with a list that has at least two elements, like:
write_data([]).
write_data([_]) :-
write('LAST!'),
nl.
write_data([X|Rest]) :-
Rest = [_|_],
write(X), nl,
write_data(Rest).
We can furthermore avoid the double unpacking with a helper predicate:
write_data([]).
write_data([H|T]) :-
write_data(T, H).
write_data([], _) :-
write('LAST!'), nl.
write_data([H|T], X) :-
write(X), nl,
write_data(T, H).

A common definition for a last/2 predicate that provides access to the last element of a list is:
last([Head| Tail], Last) :-
last(Tail, Head, Last).
last([], Last, Last).
last([Head| Tail], _, Last) :-
last(Tail, Head, Last).
When called with the first argument bound to a closed list, the auxiliary predicate, last/3, avoids spurious choice-points assuming a Prolog system implementing, as common, first-argument indexing. Can you modify this predicate to do what you want?

The general rule of thumb for cut removal is to note what was true in the clause that contains the cut , then make sure that is false in the other clause(s) .
Thus :
write_data([]).
write_data([X]) :-
/*!,*/write('LAST!'), nl.
write_data([X | Rest]) :-
dif(Rest,[]) , /**/
write(x), nl,
write_data(Rest).

Related

I have defined multiple predicates that seem to share a common form

All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?
intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
member(X,Ys),
intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
\+ member(X,Ys),
intersect(Xs,Ys,Acc).
without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :-
\+ member(X,Acc),
without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
member(X,Acc),
without_duplicates(Xs,Acc).
difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
\+ member(X,Ys),
difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
member(X,Ys),
difference(Xs,Ys,Acc).
delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
E \= X,
delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
E = X,
delete(E,Xs,Ans).
There is an abstraction for "keep elements in list for which condition holds".
The names are inclide, exclude. There is a library for those in SWI-Prolog that you can use or copy. Your predicates intersect/3, difference/3, and delete/3 would look like this:
:- use_module(library(apply)).
intersect(L1, L2, L) :-
include(member_in(L1), L2, L).
difference(L1, L2, L) :-
exclude(member_in(L2), L1, L).
member_in(List, Member) :-
memberchk(Member, List).
delete(E, L1, L) :-
exclude(=(E), L1, L).
But please take a look at the implementation of include/3 and exclude/3, here:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/apply.pl?show=src#include/3
Also in SWI-Prolog, in another library, there are versions of those predicates called intersection/3, subtract/3, delete/3:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#intersection/3
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#subtract/3
https://www.swi-prolog.org/pldoc/doc_for?object=delete/3
Those are similar in spirit to your solutions.
Your next predicate, without_duplicates, cannot be re-written like that with include/3 or exclude/3. Your implementation doesn't work, either. Try even something easy, like:
?- without_duplicates([a,b], L).
What happens?
But yeah, it is not the same as the others. To implement it correctly, depending on whether you need the original order or not.
If you don't need to keep the initial order, you can simply sort; this removes duplicates. Like this:
?- sort(List_with_duplicates, No_duplicates).
If you want to keep the original order, you need to pass the accumulated list to the recursive call.
without_duplicates([], []).
without_duplicates([H|T], [H|Result]) :-
without_duplicates_1(T, [H], Result).
without_duplicates_1([], _, []).
without_duplicates_1([H|T], Seen0, Result) :-
( memberchk(H, Seen0)
-> Seen = Seen0 , Result = Result0
; Seen = [H|Seen0], Result = [H|Result0]
),
without_duplicates_1(T, Seen, Result0).
You could get rid of one argument if you use a DCG:
without_duplicates([], []).
without_duplicates([H|T], [H|No_duplicates]) :-
phrase(no_dups(T, [H]), No_duplicates).
no_dups([], _) --> [].
no_dups([H|T], Seen) -->
{ memberchk(H, Seen) },
!,
no_dups(T, Seen).
no_dups([H|T], Seen) -->
[H],
no_dups(T, [H|Seen]).
Well, these are the "while loops" of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: Logic Programming, Functional Programming, and Inductive Definitions, Lawrence C. Paulson, Andrew W. Smith, 2001), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.
In this case, you just have a binary decision - whether something is the case or not - and you "branch" (or rather, decide to not fail the body and press on with the selected clause) on that. The "guard" (the test which supplements the head unification), in this case member(X,Ys) or \+ member(X,Ys) is a binary decision (it also is exhaustive, i.e. covers the whole space of possible X)
intersect([X|Xs],Ys,[X|Acc]) :- % if the head could unify with the goal
member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
intersect([X|Xs],Ys,Acc) :- % if the head could unify with the goal
\+ member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
Other applications may need the equivalent of a multiple-decision switch statement here, and so N>2 clauses may have to be written instead of 2.
foo(X) :-
member(X,Set1),
(...action...).
foo(X) :-
member(X,Set2),
(...action...).
foo(X) :-
member(X,Set3),
(...action...).
% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be "failure"
% of foo/1 if this clause does not exist:
foo(X) :-
\+ (member(X,Set1);member(X,Set2);member(X,Set3)),
(...action...).
Note:
Use memberchk/2 (which fails or succeeds-once) instead of member/2 (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether member(X,L).
Similarly, "cut" after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: member(X,Ys),!,...
Finally, use term comparison == and \== instead of unification = or unification failure \= for delete/3.

determine whether a list is sublist of another list in prolog

I have just write a predicate to determine whether a list is sublist of another list.
sublist( [], _ ).
sublist( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
sublist( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
But this predicate can only deal with situation like this.
1 ?- sublist([1,2,3], [0,1,0,2,0,3,0]).
true .
Can it also deal with list that its sequence is different from another list?
2 ?- sublist([['D','A','G'],['V','E','D']], [['V','E','D'],['D','A','G']]).
false.
Any help will be appreciated~~
I guess from your question that you are looking for subset and not sublist.
If each an item from the first list can have multiplicity and you want it to be succeed you can write:
subset([], _).
subset([X|Tail], Y):-
memberchk(X, Y),
subset(Tail, Y).
First clause is the base case, and second clause checks whether the item is in Y, and proceeds recursion with the tail from the first list and the second list as-is.
If each item in the first list "consumes" an item from the second list, then you would write:
subset([], _).
subset([X|Tail], Y):-
select(X, Y, Z),
subset(Tail, Z).
In this case, the second clause uses select/3 so that the matching element is removed from the second list upon recursion.

Prolog: Removing Duplicates

I am trying to remove duplicate entries from a list in prolog. So a list [a,b,a,c,b,a] would return [a,b,c]. I can not use any built in functions. I searched here and found this code.
member(X,[X|_]) :- !.
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :- not(member(H,T)), set(T,Out).
set([H|T],Out) :- member(H,T), set(T,Out).
But that would take my list and return [c,b,a] not [a,b,c]
I have remove code that will take an element and a list and return a list with occurrences of that element in the list removed. So I tried to incorporate that into my remove duplicate method but I don't really understand prolog very well so it is not working. Logically I want to take a list cons the head with the recursive call on the new list minus all occurrences of the head. This is what the code would look like in sml.
fun remv(_,nil) = nil
| remv(a,x::xs) = if x=a then remv(a,xs) else x::remv(a,xs);
fun remvdub (nil) = nil
| remvdub(x::xs) = x::remvdub(remv(x,xs));
So this is what I tried in prolog
remv(_,[],[]).
remv(X,[X|T],Ans) :- remv(X,T,Ans).
remv(X,[H|T],[H|K]) :- remv(X,T,K).
remvdub([],[]).
remvdub([H|T],[H|Ans]) :- remvdub(Ans1,Ans), remv(H,T,Ans1).
What am I missing?
% An empty list is a set.
set([], []).
% Put the head in the result,
% remove all occurrences of the head from the tail,
% make a set out of that.
set([H|T], [H|T1]) :-
remv(H, T, T2),
set(T2, T1).
% Removing anything from an empty list yields an empty list.
remv(_, [], []).
% If the head is the element we want to remove,
% do not keep the head and
% remove the element from the tail to get the new list.
remv(X, [X|T], T1) :- remv(X, T, T1).
% If the head is NOT the element we want to remove,
% keep the head and
% remove the element from the tail to get the new tail.
remv(X, [H|T], [H|T1]) :-
X \= H,
remv(X, T, T1).
The snippet of Prolog code that you posted is logically correct. If you would like to keep the first, as opposed to the last, copy of each duplicated item, you can change your code as follows:
member(X,[X|_]) :- !.
member(X,[_|T]) :- member(X,T).
set(A,B) :- set(A, B, []).
set([],[],_).
set([H|T],[H|Out],Seen) :- not(member(H,Seen)), set(T,Out, [H|Seen]).
set([H|T],Out, Seen) :- member(H,Seen), set(T,Out,Seen).
The idea is to add a third parameter, which represents the list of items that you have seen so far, and check the membership against it, rather than checking the membership against the remaining list. Note that set/2 is added to hide this third argument from the users of your predicate.
Demo on ideone.

Prolog how to print first 3 elements in a list

How can I print the first 3 elements in a list.
I have a print method
print([]).
print([X]) :- !, write(X).
print([X|T]) :- write(X), write(', '), print(T), nl.
In Prolog, the typical way to implement iteration is recursion:
print(0, _) :- !.
print(_, []).
print(N, [H|T]) :- write(H), nl, N1 is N - 1, print(N1, T).
If we reached zero or have an empty list, do nothing. If we should do something, print the first item in the list, compute the new N and recursively call itself.
The cut (!) in the first clause is necessary, otherwise we would need a condition for N in the last one.
If you always have at least tree elements ist very simple
print_first_three([A,B,C|_]) :- print(A), print(B), print(C).

SWI Prolog - conditional NOT?

I'm trying to make a prolog function. The function reads in a sentence, and then tries to extract a key word. If a key word is found, it prints a message. I want it to also print a message if no keywords are found. Here is my example :
contains([word1|_]) :- write('word1 contained').
contains([Head|Tail]) :- Head \= word1, contains(Tail).
contains([word2|_]) :- write('word2 contained').
contains([Head|Tail]) :- Head \= word2, contains(Tail).
contains([word3|_]) :- write('word3 contained').
contains([Head|Tail]) :- Head \= word3, contains(Tail).
The above code will check and see if the extracted word is present. But it does not give an answer if the words 'word1,word2 or word3' are not contained. Does anybody know how I should go about implementing this?
I tried adding :
contains([_|_]) :- write('nothing contained'),nl.
contains([Head|Tail]) :- Head \= _, contains(Tail).
But clearly this is the wrong thing to do.
The standard way to write the main part of your contains predicate is:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail).
Which means:
when you find a word, don't search any further (this is what the cut (!) operator is for).
when nothing else worked, recurse on tail.
To add an answer in case nothing is found, just add another cut on the recursive call, so that the later case is only called when nothing else (including recursion) worked:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail), !.
contains(_) :- write('Nothing found').
In imperative language you'd use some kind of flag; for example:
found = False
for word in wordlist:
if word in ('car', 'train', 'plane'):
print "Found: " + word
found = True
if not found:
print "Nothing found."
You can implement this flag as another parameter to your clauses:
% entry point
contains(X) :- contains(X, false).
% for each word...
contains([Word|Rest], Flag) :-
Word = car -> (write('Car found.'), nl, contains(Rest, true)) ;
Word = train -> (write('Train found.'), nl, contains(Rest, true)) ;
Word = plane -> (write('Plane found.'), nl, contains(Rest, true)) ;
contains(Rest, Flag).
% end of recursion
contains([], true).
contains([], false) :- write('Nothing found.'), nl.
If you want to make distinct clause for each word (and abstract the loop), change the middle part to:
% for each word...
contains([Word|Rest], Flag) :-
checkword(Word) -> NewFlag=true ; NewFlag=Flag,
contains(Rest, NewFlag).
% and at the end:
checkword(car) :- write('Car found.'), nl.
checkword(plane) :- write('Plane found.'), nl.
checkword(train) :- write('Train found.'), nl.
Here is how I would do this:
contains(Words) :-
findall(Word,has(Words,Word),Sols),
print_result(Sols).
% Word is a target word in the list Words
has(Words,Word) :-
member(Word,Words),
member(Word,[word1,word2,word3]).
print_result([]) :- write('Nothing found.\n').
print_result([X|Xs]) :- print_sols([X|Xs]).
print_sols([]).
print_sols([X|Xs]) :-
concat(X, ' contained.\n',Output),
write(Output),
print_sols(Xs).
The advantage of this approach is that it uses a higher level of abstraction, making the predicate easier to read. Since there is just one list of target words, it also becomes easier to maintain, rather than having to add a separate clause for each new word.
The trick is with the has predicate which uses member/2 twice; once to select an item from the input list, and a second time to test that it is one of the target words. Using this as an argument to findall/3 then yields all the target words that were found in the input list.
Note: The [X|Xs] in print_results just avoids having to use a cut in the first clause.
I think that liori has the best answer. Here is a slightly different approach that might make sense in some cases, i.e.:
generate a print-out
if the print-out is empty then print "Nothing found", otherwise output the print-out
The following works in SWI-Prolog and probably not in other Prologs because it uses with_output_to/2:
% Define what are the keywords
keyword(word1).
keyword(word2).
keyword(word3).
% Define how the found keywords are pretty-printed
print_keyword(W) :-
format("Found: ~w.~n", [W]).
% Generate a print-out and output it unless its empty
print_keywords(Words) :-
with_output_to(atom(PrintOut),
forall((member(W, Words), keyword(W)), print_keyword(W))),
(
PrintOut == ''
->
writeln('Nothing found.')
;
write(PrintOut)
).

Resources