Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog return a list which contains only elements which are equal to head of the list

Tags:

prolog

Hello I would like to ask a doubt I have with the following code:

principio([],[]).
principio([H],[H]).
principio([H,_|_],[H]).
principio([H,H|C],P) :-
    principio([H|C],R),P=[H|R].

I would like a way to get from:

?- principio([222,333,101,202,12,222,13,222],X).

X = [222,222,222]

But in this moment I get just the head:

X = [222]

So, to keep it clear I'd like: all successive occurrences of the first element as a list.

My doubt is what does this assignment P=[H|R] why not to put just:

principio([H,H|C],P) :-
    principio([H|C],P)

Also, how would you try to modify this to get the result I asked for? Thank you

like image 532
Enoy Avatar asked May 07 '17 14:05

Enoy


People also ask

How do I get the head of a list in Prolog?

In Prolog list elements are enclosed by brackets and separated by commas. Another way to represent a list is to use the head/tail notation [H|T]. Here the head of the list, H, is separated from the tail of the list, T, by a vertical bar.

How do you check if a list contains a value in Prolog?

If you want to check if a list contains a member, use memberchk/2 . so memberchk(I/J, Item) succeeds if I/J is in the list Item . Your include/3 predicate has no base case, and attempt to ensure that every element of the given list is I/J , so it will always fail.

How do you check if two lists have the same Prolog?

Try this: same(X, X). same([A| B], [C| D]):- A=C, same(B,D). it'll return true if they are the same.


2 Answers

Here is two ways how you can narrow down the problem. 1st, start from an unexpectedly failing query. 2nd, start from a query that should fail but rather succeeds.

1st Diagnose unexpected incompleteness

Determine a most specific failing query

?- principio([222,333,101,202,12,222,13,222],[222,222,222]).
false.

Generalize the query

... as much as possible. I could do this manually, or I could let Prolog do the work for me. Here I use library(diadem):

?- use_module(diadem).
   true.

?- principio([222,333,101,202,12,222,13,222],[222,222,222]).? Gen.
   Gen = principio([222, 333|_], [_, _|_])
;  Gen =  (dif(A100, B100), principio([A100, B100|_], [_, _|_]))
...

In other words: Not only does your original query fail, but also this generalization fails! Here, we only insist that the first two elements are different, and that the resulting list contains at least two elements — no matter which!

?- dif(X, Y), principio([X,Y|_],[_,_|_]).

Generalize your program

:- op(950, fy, *).
* _P_0.

principio([], _/*[]*/).
principio([_H], _/*[H]*/).
principio([H,_|_],[H]).
principio([H,H|C],P) :-
    * principio([H|C],R),
    * P=[H|R].

The error must reside in the little remaining part of your program. No need to read any further!

The problem is that for a list starting with two different elements you only have the clause principio([H,_|_],[H]).. So this part has to be generalized somehow.

2nd Diagnose unexpected unsoundness

Another way of finding the error would be to start with the unexpected solution:

?- principio([222,333,101,202,12,222,13,222],[222]).
true.         % incorrect !!

And then reduce the size of the query as much as possible.

?- principio([222,222],[222]).
true.         % incorrect !!

Now, specialize your program inserting false as long as above query succeeds:

principio([],[]) : - false.
principio([H],[H]) :- false.
principio([H,_|_],[H]).
principio([H,H|C],P) :- false,
    principio([H|C],R),
    P=[H|R].

The remaining visible part is the culprit! We have to revise it. What it says is:

Any list starting with two elements corresponds to the list with the first element only.

principio([],[]).
principio([H],[H]).
principio([H,D|Xs], [H|Hs]) :-
   dif(H,D),
   principio([H|Xs],[H|Hs]).
principio([H,H|Xs],[H|Hs]) :-
   principio([H|Xs],Hs).
like image 119
false Avatar answered Oct 21 '22 10:10

false


In addition to the very nice answer provided by @false (+s(0)), I would point out the possibility to use DCGs for the task. They usually yield easily readable code when describing lists (see comments beside the grammar rules):

principio([H|T],Hs) :-
   phrase(heads([H|T],H),Hs).

heads([],_H) -->          % in the empty list
   [].                    % there's no element matching H
heads([H|Xs],H) -->       % if the head of the list matches H
   [H],                   % it's in the list
   heads(Xs,H).           % same for the tail
heads([X|Xs],H) -->       % if the head of the list is
   {dif(X,H)},            % different from H it's not in the list
   heads(Xs,H).           % same for the tail

Thus your example query yields the desired result:

   ?- principio([222,333,101,202,12,222,13,222],X).
X = [222,222,222] ? ;
no
like image 41
tas Avatar answered Oct 21 '22 10:10

tas