I am trying to write a simple prolog program where a number is compared with the elements in a list. To check that the number is greater than which elements in the list, and it simply returns the list with the elements that are less than the number. Example: gt(12,[19,4,6,22],L), this should return L = [4,6].
Here is my attempt:
gt(_,[],[]):-!.
gt(Num,[H|T],[H|L]):-
Num>H,
gt(Num,T,L).
gt(Num,[H|T],[H|L]):-
Num=<H,!,
gt(Num,T,L).
The problem that I am facing is in the =< predicate. If the condition is =< the number, then I am trying to say that ignore and move to the next number gt(Num,T,L). The place where I have written L, what should I write here so that it understands that I don't want you to write that value in the list?
Works alright for values that are already smaller than the number.
?-gt(12,[6,7,6],L).
L = [6,7,6]
But fail for such tests:
?- gt(12,[19,6,7],L).
L = [19, 6, 7]
The problem is in the third parameter in the third clause of gt/3. It is also "adding" the current item H as the head of the output list.
You should write
gt(Num,[H|T],L):-
Num=<H,!,
gt(Num,T,L).
instead.
You may also get rid of those cuts (!) to make your procedure work with other modes for the parameters and/or with uninstantiated variables.
Related
I am a new programmer in Prolog and i tried to do a program that says: make the predicate penta(X), where X is a list and returns true when in X there are 5 consecutive elements where : the first element is the sum between the first and the second. Also the third element is the difference between the 5th and the 4th for example: X = [ ... 5, 7, 12, 18, 30, ... ].
So I did this:
penta(X) :-
\+length(X,0), //here i verify if the lists contains less than 5 elements so it gives false.
\+length(X,1),
\+length(X,2),
\+length(X,3),
\+length(X,4),
(A, B, C, D, E | X),
C is A + B,
C is D - E,
penta(X).
This actually does not compile so it doesn't work yet. Tell me what's wrong with it if you would like.
thank you very much.
In prolog, we write predicates not functions. A predicate defines a rule which will succeed or fail on sets of instantiated variables. So you want a predicate, penta(X) that succeeds if X is a list that contains 5 consecutive elements meeting your criteria.
Start from the top. Either the 5 consecutive elements that meet the criteria are at the head of your list, or they are later in the list.
% Succeed if the first 5 elements meets the criteria
penta([A, B, C, D, E |_]) :-
... % what goes here for this to succeed?
% Succeeds if the rest of the list succeeds, without the first element
penta([_|T]) :- penta(T).
I think these are the only two rules you need. Anything else you query that doesn't match these will fail by default, which is what you want. You don't need to check for the length for the 0 through 4 length cases. Those cases will fail the above predicates.
You'll notice that, depending upon how you implement the above, it might succeed several times. That is, it may find more than one solution. You need to decide if that's what you want, or if you want it to stop after one solution. I'll leave that as further exercise.
I've been working on Prolog for a few weeks right now. I am now trying to write a function in it called matching:
Write a predicate called matching with three parameters, all lists.
The third list must contain the index of the positions in which
the first two lists contain the same value.
If I run
matching([10,71,83,9,24,5,2],[8,71,26,9],Positions).
The results are:
?- matching([10,71,83,9,24,5,2],[8,71,26,9],Positions).
Positions = [] ;
Positions = [] ;
Positions = [_2420] ;
Positions = [_2420] ;
Positions = [_2420, _2432];...
The correct answer would be that Positions is bound to [1,3]. I have no idea what is wrong with my code. Any hint is appreciated.
A hint? Each of your matchingHelper clauses contains a mistake!
OK, a little more than a hint:
Base cases
Prolog should be giving you a warning about singleton variables here. ListofIndex is a variable, but it is only used in one place. Essentially this means that there is absolutely no constraint on this, and thus can be anything.
The correct thing would be that if either of the input lists is empty, the output is also empty.
matchingHelper([], _, , []).
matchingHelper(, [], _, []).
Equal case
This one you almost have correct, but the way you deal with ListOfIndex is backwards. You construct a NewListOfIndex based on the predicate arguments, and use that in the recursive call. The problem is that the ListOfIndex is actually the output! So you should instead construct the ListOfIndex based on the output from the recursive call.
matchingHelper([X|Xs], [X|Ys], Index, [Index|ListofIndex]) :-
Index2 is Index + 1,
matchingHelper(Xs, Ys, Index2, ListofIndex).
Unequal case
Just 2 little issues with this one. First is that this clause should only apply if X and Y are different. Just using a different variable name does not enforce this. Because there is a previous clause which handles the equal case, the first result prolog finds would be correct, but it will continue to find other, incorrect solutions because of this.
The second issue is that you don't increment the index. If you ignore the first element, the current index has to be incremented to reflect the current position.
matchingHelper([X|Xs], [Y|Ys], Index, ListofIndex) :-
X \= Y,
Index2 is Index + 1,
matchingHelper(Xs, Ys, Index2, ListofIndex).
Here's a sample run:
?- matching([10,71,83,9,24,5,2],[8,71,26,9],Positions).
Positions = [1, 3]
false
Hello I want to make a program in Prolog, that given a list of numbers and a number, it appends all the concurences of position of the number in a second list.
For example for the list (5,10,4,5,6,5) and number =5 the new list should be
(1,4,6)
here is my code so far
positions(X, [X|_],1).
positions(X, [P|T], N) :- positions(X, T, N1), N is N1+1.
find(X, [H|T] ,Z) :-positions(X,[H|T],N) , append([],N,Z).
the positions returns the first concurrence of X in the list, but I don't know how to proceed. Can you help me?
If it's not an assignment, then you can benefit from using the built-ins findall/3 and nth1/3:
?- findall(Nth, nth1(Nth, [5,10,4,5,6,5], 5), Nths).
Nths = [1, 4, 6].
Taking just the nth1 phrase, and running that, you can see it is backtracking and finding multiple solutions, then we just use findall to collect them into a list.
?- nth1(Nth, [5,10,4,5,6,5], 5).
Nth = 1 ;
Nth = 4 ;
Nth = 6.
nth1/3, when using a variable for the first parameter, is saying 'give me a list index where where the 3rd parameter is found in the list of the second parameter.
You have some good ideas, but I would suggest a couple things:
1) In Prolog, it can be beneficial to give variables meaningful names
2) Use an accumulator and you will only need positions and append
3)Use a different base case
positions([Num|List],Num,[Index|SubResult],Index) :- Index2 is Index+1,
positions(List,Num,SubResult,Index2).
positions([NotNum|List],Num,Result,Index) :- NotNum \= Num,
Index2 is Index+1,
positions(List,Num,Result,Index2).
positions([],Num,[],Index).
In our first general case, we can see the numbers match, so we go find how many results are in our sublist, which we will call the SubResult and then push the current index on to our SubResult
The next general case, the numbers do not unify, and our Result IS the SubResult, so let's call them the same thing.
In our final case (the base case) we can see the list is empty, in this case we return an empty list as we cannot match against an empty list.
You can see that the above rules are order-independent, which is something very valuable in Prolog. This means you can arrange the rules in any order, and the semantics of your Prolog program remain unchanged. Using unification to achieve this will prevent future pain in debugging.
We can wrap our predicate in the following way
positions(Num, List, Positions) :- positions(List, Num, Positions, 1).
This will allow for queries of positions(5,[5,10,4,5,6,5],Positions).
I am having troubles counting the number of lists in a nested list.
count_lists([H|T],R):-
atomic(H),!,
count_lists(T,NR),
R is NR+1.
count_lists([[H|T]|Rest],R):-
!,
count_lists([H|T],R1),
count_lists(Rest,R2),
R is R1+R2.
count_lists([],0).
First of all, I try the basic case where an element in the list is atomic and thus, I should increment the counter by one. (Also, I tried removing the atomic predicate because I figured that because of it, my code will compute the number of elements in a nested list, but it still doesn't work)
Then, if the first element is a list itself, I go recursively on it and on the remaining list, adding the results.
And the third clause is states that the number of nested lists in an empty list is 0.
?count_lists([[1,5,2,4],[1,[4,2],[5]],[4,[7]],8,[11]],R).
should return 8 but instead, returns 12.
I know it's been a while since you asked this, but here is the answer I think you were looking for:
count_lists([],1).
count_lists([H|T],Rez):-atomic(H),!,count_lists(T,Part),Rez is Part.
count_lists([H|T],Rez):-count_lists(H,Part1),count_lists(T,Part2),Rez is Part1+Part2.
This way, you count only the number of lists and not the number of elements within.
you need to distinguish lists from other elements, i.e.
count_lists(E,R):-
is_list(E),!,count_elems(E,N),
R is N+1.
count_lists(_,0).
count_elems([H|T],R):-
count_lists(H,Hc),
count_elems(T,Tc),
R is Hc+Tc.
count_elems([],0).
but the code is contrived, using library we can get it done in 1 step:
count_lists(E, R):-
maplist(count_lists, E, Cs) -> sum_list(Cs, S), R is S+1 ; R = 0.
the code can be understood only WRT maplist/N behaviour
?- maplist(_,a).
false.
?- maplist(_,[]).
true.
?- maplist(_,[1]).
ERROR: apply:maplist_/2: Arguments are not sufficiently instantiated
In your solution you forget that e.g. [1,2,3] = [1,2,3| []] or [1,2,3] = [1| [2| [3| []]]]. Thus, you're "over-counting", thanks to your first clause. For example:
?- count_lists([1,2,3], N).
N = 3.
But there's another problem. In your second clause, if you've a nested list that nests other lists, you don't count it. Not clear from the title if that's intended or if it's a bug.
You shouldn't have complicated yourself.
count([],1).
count([L1|L2],Rez):- count(L1,Rez1),count(L2,Rez2),Rez is Rez1+Rez2.
You take out all the elements in a list recursively until you are left out with the empty list which values 1.
I'm trying to write a predicate that will take a list, a number, and a variable that will then give the first N elements of the list to the variable. I'm able to do this, but while trying to make it more flexible so that it will stop when it reaches the end of the list or when N is 0, I started getting a garbage variable returned at the end of my list.
Here is my code:
take(_,0,_) :- !.
take([],_,_) :- !.
take([Head|Tail], Number, [Head|Result]) :-
Number > 0,
N1 is Number - 1,
take(Tail, N1, Result).
and when I try to use take([1,3,5,7], 3, L1), I get L1 = [1, 3, 5|_G2028].
I think it has something to do with how I didn't define Result as an empty list, but I don't know how to do so, while keeping the functionality of the predicate.
Change the first 2 rules to:
take(_,0,[]) :- !.
take([],_,[]) :- !.
From your code I can see that take is a predicate that returns true when the 3rd argument is the result of taking the leading elements from the list in the first argument as many as possible but less than the number specified 2nd argument.
So you can constraint the output when 2nd argument is 0 to [], since you have reached the limit.
And you can constraint the output when the 1st argument is an empty list to [], since you can't take anything anyway.
Without the constraints above, it means that anything is valid when the maximum number of elements to take is 0 or when the original list is empty.
Alternatively, you can write everything in one rule:
take(In, Num, Out) :- append(Out, _, In),
length(Out, OutLen), length(In, InLen),
OutLen is min(Num, InLen), !.
The length of the output list is the minimum between the length of original list and Num, that is used as constraint to the append to take out exactly the appropriate number of elements from the original list.
While the existing answer tells you how to fix it, I'd like to try to explain what that "garbage variable" is.
I get L1 = [1, 3, 5|_G2028].
That variable in your list, _G2028, is an uninstantiated variable. The interpreter knows that something is there, but doesn't know what the something is.
This is happening because of the first two clauses,
take(_,0,_) :- !.
take([],_,_) :- !.
The first one says, paraphrased, if you take 0 items from anything, you can get anything.
The second one says, if you take any number of items from an empty list, you can get anything.
So the interpreter gets to one of these, because it wanted to evaluate take(Tail, N1, Result). At that point, Tail is known, N1 is known, but Result is yet unknown. After calling either of these clauses, the interpreter is none the wiser, since neither tells it what Result is. But the goal succeeded, so it reports back and tells you that L1 = [1, 3, 5|_G2028].