0

I'm trying to make a small prolog program which can delete a particular number from a list and I cannot seem to make it result in a list.

The code goes by

delete_all(X,[H|T],Ans):- X\=H ,Ans= H; delete_all(X,T,Ans).

When I input

| ?- delete_all(x,[a,b,x],Answer).

I get

Answer = a ? ;
Answer = b ? ;
no

So far I tried

| ?- findall(Ans,delete_all(x,[a,b,x],Ans),Ans).

Which returns

Ans = [a,b]

Yes, I want this output but I do not wish to change the form of my query. Is there any other way to do this?

I'm sorry if I'm missing out the basics, really new to this.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Lester
  • 83
  • 3
  • 10

1 Answers1

0

Since you said you were quite new to Prolog, I think it might be useful if we first evaluate your code instead of just immediately providing a working sample:

delete_all(X,[H|T],Ans):-
    X \= H, Ans = H ;
    delete_all(X,T,Ans).

In words, this says:

  • X is not equal to H, if so, unify Ans with H

    OR

  • recursively do same for tail

This explains why your program gives you separate results when the element was not equal to X and returns false whenever the element was encountered in the list. To explain a bit further why you get false in the latter case, let's examine 2 small examples of execution:

delete_all(e,[a,e],Ans)

CASE e \= a   ->   Ans = e     (Prolog returns you Ans)


delete_all(a,[a],Ans)

CASE a = a    ->   the OR operator triggers and recursion starts

But oops, we now enter this scenario:

delete_all(a,[],Ans)

At this point, Prolog fails to match any of your code because the list is empty and you do not specify this anywhere. Can you see and understand what is happening (and going wrong) ?

Now that we've evaluated your code, let's try and write a model in which we can express what you are trying to accomplish. It is always a good idea to break any problem down into smaller pieces, so let's start:

  • We can try to delete something in an empty list -> this should always be true

  • We can try to delete something in a non-empty list, but we want the actual resulting List to be returned, not the deleted element. To do this, we will need to keep track of every element that didn't need deletion throughout recursion and just skip the elements that we wanted to delete.

We'll start with the base case:

% Base case for empty list (_ = wildcard, since we do not care about
% which element we want to delete out of an empty list)
delete_all(_,[],[]).

Now for the cases in which we do need to process elements:

% X is not equal to H, so we want to preserve H in our result list
delete_all(X,[H|T],[H|Ans]) :-
    ...
    ...


% X is equal to H, so we want to skip H and not add it to our result list
delete_all(X,[H|T],Ans) :-
    ...
    ...

As you can see, we now have a very easy-to-read and structured model for your problem. All that is left now is to write the actual conditions and recursive calls. I thought it might be a good idea for you to try and write them yourself, as that is the best way to learn!

Also, don't be sorry, everyone has to start somewhere, that's why everyone's here to help. Let me know if you get it working, if not, I'll edit this post to specify more information.

Good luck!

EDIT

You're very close now, this just leaves the last bit of explaining. Consider the following:

% Prepend H to our current result being built recursively
recursion([H|T],[H|Result]):-
    recursion(T,Result).       % Call recursively with the result tail

So what this does is keep prepending every H in front of our result and then make the actual recursive call with our result tail. It's like thinking backwards.

Let's break it down:

recursion([a,b,c],Result)

% Execution:

(1) case H = a : prepend a to result and call recursively with result tail
(2) case H = b : prepend b to result and call recursively with result tail
(3) case H = c : prepend c to result and call recursively with result tail

Now we reach the point where our input list becomes empty, and this explains why we need our base case:

recursion([],[]).

This case says: whenever our input list is empty, we are going to prepend to an empty list to get our desired result. If you are wondering why an empty list, that is just how Prolog works:

[a,b,c] is equivalent to [a,b,c|[]]

You can read some more about this on this and this question.

So to summarize (and still not explicitly give you the answer :) ), you wrote the conditions correctly, the base case is also present, the problem lies in how you make your recursive call. Can you figure it out? Keep me posted!

EDIT

The difference between your attempt and a working solution is the recursive call you're making. If you do:

recursion([H|T],R) :-
    recursion(T,[H|R])

You will end up with a reversed list, because you make the recursive call prepending H to R, which is not what we want in this case. That is why we specify the prepend in the head of delete_all and -and this is why your code isn't working- call recursively with just the result. That is also why we write the base case with an empty list. I'll give you a quick example:

% We prepend H to our result in the recursive call
recursion([],R)    :- write('Ended with result: '), writeln(R).
recursion([H|T],R) :-
    recursion(T,[H|R]).  %   <-  here

Outputs:

?- recursion([1,2,3],R).
   Ended with result:
   [3, 2, 1|_G5835]    % <- note the uninstantiated var and reversed order

Correct example:

% We prepend H to our result in the head of recursion2
recursion2([],[]).
recursion2([H|T],[H|R]) :-     % <-  here
    recursion2(T,R).           % and recursive call with just R

This outputs:

?- recursion2([1,2,3],R).
   R = [1, 2, 3]

So you only need to edit your recursive call(s), instead of prepending H to your result there, do it in your head, and just pass your Ans var in the recursive call.

% We prepend H to Ans in the head of delete_all
delete_all(X,[H|T],[H|Ans]) :-
    ...
    delete_all(X,T,Ans).    % <- and make the recursive call with just Ans

That should fix it :).

Community
  • 1
  • 1
SND
  • 1,552
  • 2
  • 16
  • 29
  • is [this](http://prntscr.com/ahmof4) wrong? I keep getting a 'no' – Lester Mar 20 '16 at 14:20
  • I edited my answer to try and explain why your code isn't working yet. Let me know if you get it working. – SND Mar 20 '16 at 14:40
  • is it a problem with termination? i kept re-reading but don't get what I should be changing? you said prepend an empty list to the end of the answer but I have no idea how to do that... – Lester Mar 20 '16 at 15:06
  • See the following edit. I just noticed it has become quite a long post, sorry for that, I let myself go there a bit too much :). – SND Mar 20 '16 at 16:26
  • Thanks! I get it now. I sincerely appreciate your help. I hope you have a nice day. Side question: I still dont get why prepending in the head doesnt produce a reversed list. – Lester Mar 21 '16 at 05:01
  • Yes it is indeed quite tricky to fully understand this when you're just starting out with Prolog or the concept of recursion in general. I think maybe the best way to learn it yourself is to trace your program's execution. You can enable this by running the command 'trace' (and disable it again by running 'notrace'). When trace is enabled, prolog will explicitly show you what is happening between each step in the program, this is a great way to understand the concept of recursion. – SND Mar 21 '16 at 15:35