I am completely confused with Prolog. I have two simple predicates that I am trying to figure out, but can't seem to get them right. First I am trying to figure out a variation of a delete predicate where I need to delete all occurrences of a given element from a List1, and see if it is equal to List2.
This is what I tried.
del(S,[P],[P]).
del(S,[S],[]).
del(S,[H1,H2|T],L2) :- H1 = S, del([H2|T],L2).
del(S,[H1,H2|T],[H1,T2]) :- H1 \= S, del([H2|T],T2).
The other predicate takes two lists and takes all occurrences of the element 2 in List1 and replaces it with 4 and sees if List1 is the same as List2. To make is simplier, there's no sublists.
These are what I have tried.
change([],[]).
change([H1,H2|T],L) :- H1 = 2, append([2],L), change([H2|T],L).
change([H1,H2|T],L) :- H1 \= 2, append(H1,L), change([H2|T],L).
I would like to know what errors I have made and perhaps an explanation of how these terms are suppose to work. Thank you.
Examination of del/3
(1) del(S,[P],[P]).
(2) del(S,[S],[]).
(3) del(S,[H1,H2|T],L2) :- H1 = S, del([H2|T],L2).
(4) del(S,[H1,H2|T],[H1,T2]) :- H1 \= S, del([H2|T],T2).
(1) This rule says that the list [P]
is the list [P]
with the element S
removed. The problem with this rule is that S
and P
could be the same value, and so the rule isn't always true. If you wanted this to be true all the time (be a valid rule), you'd need to stipulate that S
and P
can't be instantiated to the same value:
del(S, [P], [P]) :- S \= P.
That's assuming we even need this rule, but we'll leave it here for the time being as, in this new state, it's correct.
(2) This rule says that []
is the list [S]
with the element S
removed. That is true.
(3) This rule says that L2
is the list [H1,H2|T]
with S
removed if H1
and S
are unified and L2
is [H2|T]
with S
removed. BUT, there's a missing argument. You have del([H2|T], L2)
but the del/3
is defined to take 3 arguments. We'll assume you meant S
is the first argument (S
is still what's being removed), so del(S, [H2|T], L2)
. The newly repaired rule appears to be logical.
(4) This rule says that *[H1,T2]
is the list [H1,H2|T]
with the element S
removed if H1
is not unifiable with S
and T2
is the list [H2|T]
with the element S
removed. This has the same problem as #3 with the missing argument to del/3
which we'll, again, assume is S
, making it, del(S, [H2|T], T2)
. Another problem is that you have [H1,T2]
which is a list of only two elements: H1
and T2
. Another typo. This should be [H1|T2]
. So the repaired rule seems to make sense now.
Just fixing these couple of careless errors causes your predicate to almost work! It will yield the correct result when the first argument is instantiated:
| ?- del(a, [a,b,c,a,d,a], L).
L = [b,c,d] ? a
no
Also, it can be cleaned up a bit. The H2
isn't really used in the 3rd and 4th clauses. And in the 3rd clause you can instantiate S
and H1
in the head of the predicate. So those two become:
(3) del(S, [S|T], L2) :- del(S, T, L2).
(4) del(S, [H|T], [H|T2]) :- H \= S, del(S, T, T2).
The predicate fails on the list being empty. I'm not sure if this is intentional, but you could argue that del(X, [], [])
should be true (an empty list results when you remove an element from the empty list). If we include this rule:
(1a) del(_, [], []).
We can now get rid of rules (1) and (2) because (3) and (4) will take care of them and recurse down to rule (1a).
Additionally, the rules still fail on this case:
| ?- del(X, [a,b,c], [a,c]).
no
It would be nice if this burped out, X = b
. The problem is in clause (4) where we check H \= S
which means H
and S
are not unifiable. This expression works against us if S
is a variable because then H \= S
will always fail (since S
could indeed be unified to H
). So we replace it with dif(H,S)
to check if these terms are the same.
Some Prologs don't offer dif/2
, in which case, you could substitute \==
for this solution (H \== S
). Our resulting rule set is:
(1) del(_, [], []).
(2) del(S, [S|T], L2) :- del(S, T, L2).
(3) del(S, [H|T], [H|T2]) :- dif(H, S), del(S, T, T2).
Let's "reread" these rules:
[S|T]
with the element S
removed is the list L2
if L2
is the list T
with the element S
removed.[H|T2]
is the list [H|T]
with the element S
removed if S
is different than H
and T2
is the list T
with the element S
removed.That looks a lot simpler although just a couple of simple reworks away from the original. It will now produce this result, which is nice:
| ?- del1(X, [a,b,c,b,d], [a,c,d]).
X = b ? ;
(1 ms) no
Examination of change/2
(1) change([],[]).
(2) change([H1,H2|T],L) :- H1 = 2, append([2],L), change([H2|T],L).
(3) change([H1,H2|T],L) :- H1 \= 2, append(H1,L), change([H2|T],L).
(1) This rule says if we change all the 2's in the empty list, we get the empty list. Sounds good.
(2) This rule says if I take the list [H1,H2|T]
and I change the 2's to 4's, I get L
if H1
is 2 and I append L
to [2]
(which will always fail since [2]
is not variable - see the online docs for append/2
and append/3
! append/2
is for appending lists of lists, for example) and L
is [H2|T]
with 2's changed to 4's. Already this isn't making any logical sense at all. If I'm changing 2's to 4's why am I appending something to [2]
? We need to lose the append
and simply unify L
with [4|T2]
where T2
is [H2|T]
with its 2's changed to 4's. Or in other words:
(2) change([H1,H2|T], L) :- H1 = 2, L = [4|T2], change([H2|T], T2).
This can be simplified further using the method of incorporating the unification in the head of the clause as above. Also note, again, we have no real use for making H2
visible here. So [H2|T]
can just be a T
:
(2) change([2|T], [4|T2]) :- change(T, T2).
So we use a 4 instead of a 2 if there's a 2, then process the tails.
(3) This rule has the same problem (2) does with the append/2
query. We can follow the same pattern of what we did with rule (2), and fix the non-equal check:
(3) change([H|T], [H|T2]) :- H \== 2, change(T, T2).
So we just carry over the element H
if it's not a 2, then process the tails. The complete change
predicate looks like:
(1) change([], []).
(2) change([2|T], [4|T2]) :- change(T, T2).
(3) change([H|T], [H|T2]) :- H \== 2, change(T, T2).
Rereading the rules, seeing that they make logical sense:
[2|T]
is the list [4|T2]
if T2
is the list T
with the 2's changed to 4's.[H|T]
is the list [H|T2]
if H
is not 2
and T2
is the list T
with the 2's changed to 4's.