I'm trying to write a simple procedure that checks if a list has any duplicates. This is what I have tried so far:
% returns true if the list has no duplicate items.
no_duplicates([X|XS]) :- member(X,XS) -> false ; no_duplicates(XS).
no_duplicates([]) :- true.
If I try no_duplicates([1,2,3,3]). It says true. Why is this? I'm probably misunderstanding Prolog here, but any help is appreciated.
To answer your questions: your solution actually fails as expected for no_duplicates([1,2,3,3]). So there is no problem.
Now take the queries:
?- A = 1, no_duplicates([A, 2]).
A = 1.
?- no_duplicates([A, 2]), A = 1.
They both mean the same, so we should expect that Prolog will produce the same answer. (To be more precise we expect the same ignoring errors and non-termination).
However, four proposed solutions differ! And the one that does not, differs for:
?- A = 2, no_duplicates([A, 2]).
false.
?- no_duplicates([A, 2]), A = 2.
Note that it is always the second query that makes troubles. To solve this problem we need a good answer for no_duplicates([A, 2]). It cannot be false, since there are some values for A to make it true. Like A = 1. Nor can it be true, since some values do not fit, like A = 2.
Another possibility would be to issue an instantiation_error in this case. Meaning: I have not enough information so I better stop than mess around with potentially incorrect information.
Ideally, we get one answer that covers all possible solutions. This answer is dif(A, 2) which means that all A that are different to 2 are solutions.
dif/2 is one of the oldest built-in predicates, already Prolog 0 did possess it. Unfortunately, later developments discarded it in Prolog I and thus Edinburgh Prolog and thus ISO Prolog.
However, current systems including SICStus, YAP, SWI all offer it. And there is a safe way to approximate dif/2 safely in ISO-Prolog
no_duplicates(Xs) :-
all_different(Xs). % the common name
all_different([]).
all_different([X|Xs]) :-
maplist(dif(X),Xs).
all_different(Xs).
See: prolog-dif
Here's yet another approach, which works because sort/2 removes duplicates:
no_duplicates(L) :-
length(L, N),
sort(L, LS),
length(LS, N).
I'd go at the problem more descriptively:
no_duplicates( [] ) . % the empty list is unique
no_duplicates( [X|Xs] ) :- % a list of length 1+ is unique
\+ member(X,Xs) , % - if its head is not found in the tail,
no_duplicates(Xs) % - and its tail is itself unique.
. %
Thinking on this, since this is a somewhat expensive operation — O(n2)? — it might be more efficient to use sort/2 and take advantage of the fact that it produces an ordered set, removing duplicates. You could say something like
no_duplicates( L ) :-
sort(L,R) , % sort the source list, removing duplicates
length(L,N) , % determine the length of the source list
length(R,N) . % check that against the result list
Or you could use msort/3 (which doesn't remove duplicates), might be a bit faster, too:
no_duplicates( L ) :-
msort(L,R), % order the list
\+ append(_,[X,X|_],R) % see if we can find two consecutive identical members
.
Duplicates in a list are same elements not at the same place in the list, so no_duplicates can be written :
no_duplicates(L) :-
\+((nth0(Id1, L, V), nth0(Id2, L, V), Id1 \= Id2)).
Jay already noted that your code is working. An alternative, slightly less verbose
no_duplicates(L) :- \+ (append(_, [X|XS], L), memberchk(X, XS)).
Related
Hey so this is my code so far. I am only a begginer in prolog but i need it for school
firstElement([_|_], [Elem1|List1], [Elem2|List2]):-
Elem1 =< Elem2, merge([Elem1] , List1, [Elem2|List2]);
merge([], [Elem2], List2).
merge([Head|Tail], [Elem1|List1], [Elem2|List2]):-
Elem1 =< Elem2,!, add(Elem1,[Head|Tail],[Head|Tail1]),
merge([Head|Tail1], List1, [Elem2|List2]);
add(Elem2,[Head|Tail],[Head|Tail1]),
merge([Head|Tail1], [Elem1|List1], List2).
merge([Head|Tail], [], [Elem2|List2]):-
add(Elem2,[Head|Tail],[Head|Tail1]).
merge([Head|Tail], [Elem1|List1], []):-
add(Elem1,[Head|Tail],[Head|Tail1]).
merge([Head|Tail], [], []).
add(X,[],[X]).
add(X,[Y|Tail],[Y|Tail1]):-
add(X,Tail,Tail1).
I found out that everytime it gets out of a merge it keeps forgetting the last number so it gets back to nothing in the end.
I think you’ve gotten very mixed up here with your code. A complete solution can be had without helpers and with only a few clauses.
First let us discuss the two base cases involving empty lists:
merge(X, [], X).
merge([], X, X).
You don’t quite have these, but I see some sort of recognition that you need to handle empty lists specially in your second and third clauses, but I think you got confused and overcomplicated them. There’s really three scenarios covered by these two clauses. The case where both lists are empty is a freebie covered by both of them, but since that case would work out to merge([], [], []), it’s covered. The big idea here is that if you exhaust either list, because they were sorted, what you have left in the other list is your result. Think about it.
This leaves the interesting case, which is one where we have some items in both lists. Essentially what you want to do is select the smaller of the two, and then recur on the entire other list and the remainder of the one you selected the smaller value from. This is one clause for that:
merge([L|Ls], [R|Rs], [L|Merged]) :-
L #< R,
merge(Ls, [R|Rs], Merged).
Here’s what you should note:
The “result” has L prepended to the recursively constructed remainder.
The recursive call to merge rebuilds the entire second list, using [R|Rs].
It should be possible to build the other clause by looking at this.
As an intermediate Prolog user, I would be naturally a bit suspicious of using two clauses to do this work, because it’s going to create unnecessary choice points. As a beginner, you will be tempted to erase those choice points using cuts, which will go badly for you. A more intermediate approach would be to subsume both of the necessary clauses into one using a conditional operator:
merge([L|Ls], [R|Rs], [N|Ns]) :-
( L #< R ->
N = L, merge(Ls, [R|Rs], Ns)
; —- other case goes here
).
An expert would probably build it using if_/3 instead:
#<(X,Y,true) :- X #< Y.
#<(X,Y,false) :- X #>= Y.
merge([L|Ls], [R|Rs], [N|Ns]) :-
if_(#<(L,R),
(N = L, merge(Ls, [R|Rs], Ns)),
( -- other case here )).
Anyway, I hope this helps illustrate the situation.
I'm currently studying Prolog, and in one of the notes I'm reading an example is given of how to use the cut operator correctly. Consider the following function to remove all elements of a particular value from a list.
rm(_,[],[]).
rm(A,[A|L],R) :- rm(A,L,R).
rm(A,[B|L],[B|R]) :- rm(A,L,R).
Due to backtracking, this is not a correct definition of the function, and the function will return all sublists of the list obtained from removing some elements of a particular value, but not necessarily all of them. The notes I'm reading say that a correct way to fix this is to replace the second line by the line
rm(A,[A|L],R) :- !, rm(A,L,R)
But that replacing the line by
rm(A,[A|L],R) :- rm(A,L,R), !
is not correct. I'm not sure why the second example is an incorrect way to fix the function. In swipl, replacing the second term by these fixes seems to always return the same answer on the test cases I consider. What am I missing here?
Your example is a perfect example to illustrate why using the cut here is never a good idea.
Using rm(A,[A|L],R) :- !, rm(A,L,R). makes only sense if both the first and second argument are sufficiently instantiated. But if they are insufficiently instantiated, you get an incomplete answer like:
?- rm(X, [a], R).
X = a, R = []. % incomplete
This clearly misses an answer, as it constrains X to be a only. But if X is anything else, we get a different result, namely:
?- X = b, rm(X,[a],R).
R = [a].
Using the cut at the end as in rm(A,[A|L],R) :- rm(A,L,R), !. is even worse: First, all our assumptions so far must hold, and then additionally the third argument must not be instantiated. Otherwise we get additional incorrect solutions.
?- rm(a,[a],R).
R = [].
?- rm(a,[a],[a]).
true, unexpected. % incorrect
Just recall what we are asking here:
User: When removing a from the list [a] what do we get?
Prolog: Nothing, nil, nada.
User: But can't I have instead of nothing just [a]? Please!
Prolog: OK, I give in.
That's not the way you want to implement an accounting system.
So both uses of cuts are bad. But the second one is clearly worse for it has many more preconditions to remember and is also inefficient.
On the other hand there are some cases where you can use these predicates. But typically it is quite difficult to remember when this is safe. Thus such cuts are a permanent source of errors.
Is there any hope to get rid of all this fine print? Fortunately, there is a way out using if_/3 from library(reif) for SICStus|SWI. Download it and say:
:- use_module(reif).
rm(_,[],[]).
rm(A,[X|Xs], Ys0) :-
if_(A = X, Ys0 = Ys, Ys0 = [X|Ys]),
rm(A, Xs, Ys).
This program is comparably efficient but does not have any of the aforementioned defects:
?- rm(X, [a], R).
X = a, R = []
; R = [a], dif(X, a).
Note the second new answer! It says that for all X that are different to a, the list remains unchanged.
This may seem like an odd question that could simply be answered by the findall/3 predicate. However my problem is a little deeper than that.
So I have a predicate called ran_num/1 that returns one of five random numbers (that is I do not know what the numbers could be but there are only 5 of them).
When I run the predicate it returns this output as an example:
?- ran_num(X).
X = 2
?-
Note that there are no alternative answers, pressing ; will do nothing. Prolog is awaiting another query command.
If I run findall on this the result is:
?- findall(X, ran_num(X), L).
L = [2]
?-
Is there an inbuilt predicate or method I can implement that will get me all the possible numbers that can be generated? So for example I can get a list which is [2,60,349,400,401].
Assume I cannot change the ran_num/1 predicate to give me alternatives.
The problem that you face is that ran_num/1 is deterministic as far as the interpreter is concerned. It doesn't know that calling it again may yield a different result.
Prolog's random_between/3 works like that, so I've defined your predicate ran_num/1 as follows.
ran_num(X) :-
random_between(1, 5, Y),
ran_num_mapping(Y, X).
ran_num_mapping(1, 2).
ran_num_mapping(2, 60).
ran_num_mapping(3, 349).
ran_num_mapping(4, 400).
ran_num_mapping(5, 401).
This is deterministic, like your example.
So ran_num/1 returns one of five different numbers at random and we want to know these five numbers. This means we need to keep calling ran_num/1 until we have a set of five numbers.
We can define a set S of length N.
is_set_of_length(S, N) :-
is_set(S),
length(S, N).
So we have all "ran_num"s if we have a set of length 5.
get_all_ran_nums(Y, Y) :-
is_set_of_length(Y, 5), !.
Otherwise, we get another ran_num, add it to the set, and check again.
get_all_ran_nums(L, T) :-
ran_num(X),
ord_add_element(T, X, Lout),
get_all_ran_nums(L, Lout).
We need to start this with an empty list.
get_all_ran_nums(X) :-
get_all_ran_nums(X, []).
And this yields our result.
?-
get_all_ran_nums(X).
X = [2, 60, 349, 400, 401]
?-
Please note that the cut in the first clause of get_all_ran_nums/2 is necessary, otherwise we can keep backtracking but getting the same result. Since we can keep generating ran_nums and adding them to the set; if they are already in the set, ord_add_element/3 will still succeed.
Also note that, since the numbers are generated at random, in theory this could keep running for any length of time and not getting the fifth number we need to stop.
How to implement rule1 that succeeds iff rule2 returns two or more results?
rule1(X) :-
rule2(X, _).
How can I count the results, and then set a minimum for when to succeed?
How can I count the results, and then set a minimum for when it's true?
It is not clear what you mean by results. So I will make some guesses. A result might be:
A solution. For example, the goal member(X,[1,2,1]) has two solutions. Not three. In this case consider using either setof/3 or a similar predicate. In any case, you should first understand setof/3 before addressing the problem you have.
An answer. The goal member(X,[1,2,1]) has three answers. The goal member(X,[Y,Z]) has two answers, but infinitely many solutions.
So if you want to ensure that there are at least a certain number of answers, define:
at_least(Goal, N) :-
\+ \+ call_nth(Goal, N).
with call_nth/2 defined in another SO-answer.
Note that the other SO-answers are not correct: They either do not terminate or produce unexpected instantiations.
you can use library(aggregate) to count solutions
:- use_module(library(aggregate)).
% it's useful to declare this for modularization
:- meta_predicate at_least(0, +).
at_least(Predicate, Minimum) :-
aggregate_all(count, Predicate, N),
N >= Minimum.
example:
?- at_least(member(_,[1,2,3]),3).
true.
?- at_least(member(_,[1,2,3]),4).
false.
edit here is a more efficient way, using SWI-Prolog facilities for global variables
at_least(P, N) :-
nb_setval(at_least, 0),
P,
nb_getval(at_least, C),
S is C + 1,
( S >= N, ! ; nb_setval(at_least, S), fail ).
with this definition, P is called just N times. (I introduce a service predicate m/2 that displays what it returns)
m(X, L) :- member(X, L), writeln(x:X).
?- at_least(m(X,[1,2,3]),2).
x:1
x:2
X = 2.
edit accounting for #false comment, I tried
?- call_nth(m(X,[1,2,3]),2).
x:1
x:2
X = 2 ;
x:3
false.
with call_nth from here.
From the practical point of view, I think nb_setval (vs nb_setarg) suffers the usual tradeoffs between global and local variables. I.e. for some task could be handly to know what's the limit hit to accept the condition. If this is not required, nb_setarg it's more clean.
Bottom line: the better way to do would clearly be using call_nth, with the 'trick' of double negation solving the undue variable instantiation.
I know how to iterate over lists in Prolog to find the maximum, but what if each thing is a separate clause? For example if I had a bunch of felines and their ages, how would I find the oldest kitty?
cat(sassy, 5).
cat(misty, 3).
cat(princess, 2).
My first thought was "hmm, the oldest cat is the one for which no older exists". But I couldn't really translate that well to prolog.
oldest(X) :- cat(X, AgeX), cat(Y, AgeY), X \= Y, \+ AgeX < AgeY, print(Y).
This still errorenously matches "misty". What's the proper way to do this? Is there some way to more directly just iterate over the ages to choose max?
One way is
oldest(X) :- cat(X, AgeX), \+ Y^(cat(Y, AgeY), Y \= X, AgeX < AgeY).
You can also use setof/3 to get a list of all cats and get the maximum from that.
A cat is the oldest if it's a cat and there is not a cat older than it. Let's write that in Prolog:
oldest(X):- cat(X, _), not( thereAreOlders(X)), !.
thereAreOlders(X):- cat(X, N), cat(C, M), C\=X, M > N.
If you consult:
?- oldest(X).
X = sassy.
Here is a solution that loops through all the solutions, always recording the solution that is better than the previous best. In the end, the best solution is returned.
The recording is done using assert/1, you could also use a non-backtrackable global variable if your Prolog provides that (SWI-Prolog does).
The benefit of this approach is that is considers each solution only once, i.e. complexity O(n). So, even though it looks uglier than starblue's solution, it should run better.
% Data
cat(sassy, 5).
cat(misty, 3).
cat(miisu, 10).
cat(princess, 2).
% Interface
oldest_cat(Name) :-
loop_through_cats,
fetch_oldest_cat(Name).
loop_through_cats :-
cat(Name, Age),
record_cat_age(Name, Age),
fail ; true.
:- dynamic current_oldest_cat/2.
record_cat_age(Name, Age) :-
current_oldest_cat(_, CAge),
!,
Age > CAge,
retract(current_oldest_cat(_, _)),
assert(current_oldest_cat(Name, Age)).
record_cat_age(Name, Age) :-
assert(current_oldest_cat(Name, Age)).
fetch_oldest_cat(Name) :-
retract(current_oldest_cat(Name, _Age)).
Usage example:
?- oldest_cat(Name).
Name = miisu
Miisu is a typical Estonian cat name. ;)
On a stylistic point- there are a few different approaches here (some are very elegant, others more 'readable'). If you're a beginner- chose your own, preferred, way of doing things- however inefficient.
You can learn techniques for efficiency later. Enjoy Prolog- its a beautiful language.
I don't remember much Prolog, but I do know that you shouldn't think about solving problems as you would with an imperative programming language.