1

This is the code for deleting or removing an element from a given list:

remove_elem(X,[],[]). 
remove_elem(X,L1,L2) :-
   L1 = [H|T],
   X == H,
   remove_elem(X,T,Temp),
   L2 = Temp. 
remove_elem(X,L1,L2) :- 
   L1 = [H|T],
   X \== H, 
   remove_elem(X,T,Temp),
   L2 = [H|Temp].

How can I modify it, so that I can delete every occurrence of a sub list from a list?

When I tried to put a list in an element, it only deletes the element and only once.

It should be this:

?- remove([1,2],[1,2,3,4,1,2,5,6,1,2,1],L).   
L = [3,4,5,6,1].                        % expected result
repeat
  • 19,449
  • 4
  • 51
  • 152

3 Answers3

1

This logically pure implementation is based on the predicates if_/3 and (=)/3.

First, we build a reified version of prefix_of/2:

prefix_of_t([],_,true).
prefix_of_t([X|Xs],Zs,T) :-
   prefix_of_t__aux(Zs,X,Xs,T).

prefix_of_t__aux([],_,_,false).
prefix_of_t__aux([Z|Zs],X,Xs,T) :-
   if_(X=Z, prefix_of_t(Xs,Zs,T), T=false).

Then, on to the main predicate list_sublist_removed/3:

list_sublist_removed([],[_|_],[]).
list_sublist_removed([X|Xs],[L|Ls],Zs) :-
    if_(prefix_of_t([L|Ls],[X|Xs]),                 % test
        (Zs = Zs0,     append([L|Ls],Xs0,[X|Xs])),  % case 1
        (Zs = [X|Zs0], Xs0 = Xs)),                  % case 2
    list_sublist_removed(Xs0,[L|Ls],Zs0).

A few operational notes on the recursive clause of list_sublist_removed/3:

  1. First (test), we check if [L|Ls] is a prefix of [X|Xs].

  2. If it is present (case 1), we strip it off [X|Xs] yielding Xs0 and add nothing to Zs.

  3. If it is absent (case 2), we strip X off [X|Xs] and add X to Zs.

  4. We recurse on the rest of [X|Xs] until no more items are left to process.


Onwards to some queries!

  1. The use case you gave in your question:

    ?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
    L = [3,4,5,6,1].                           % succeeds deterministically
    
  2. Two queries that try to find the sublist that was removed:

    ?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],Sub,[  3,4,5,6,1]).
    Sub = [1,2] ? ;
    no
    
    ?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],Sub,[1,3,4,5,6,1]).
    no
    
  3. Next, let's find a suitable Ls in this query:

       
    ?- list_sublist_removed(Ls,[1,2],[3,4,5,6,1]).
    % a lot of time passes ... and nothing happens!
    

    Non-termination! This is unfortunate, but within expectations, as the solution set is infinite. However, by a-priori constraining the length of Ls, we can get all expected results:

    ?- length(Ls,_), list_sublist_removed(Ls,[1,2],[3,4,5,6,1]).
      Ls = [      3,4,5,6,1]     ?
    ; Ls = [1,2,  3,4,5,6,1]     ?
    ; Ls = [3, 1,2, 4,5,6,1]     ?
    ; Ls = [3,4, 1,2, 5,6,1]     ?
    ; Ls = [3,4,5, 1,2, 6,1]     ?
    ; Ls = [3,4,5,6, 1,2, 1]     ?
    ; Ls = [3,4,5,6,1, 1,2 ]     ?
    ; Ls = [1,2, 1,2, 3,4,5,6,1] ? ...
    
Community
  • 1
  • 1
repeat
  • 19,449
  • 4
  • 51
  • 152
1

Inspired by @CapelliC's implementation I wrote the following code based on and_t/3:

append_t([]    ,Ys,Ys, true).
append_t([X|Xs],Ys,Zs,Truth) :-
   append_aux_t(Zs,Ys,Xs,X,Truth).

append_aux_t([]    ,_ ,_ ,_,false).     % aux pred for using 1st argument indexing
append_aux_t([Z|Zs],Ys,Xs,X,Truth) :-
   and_t(X=Z, append_t(Xs,Ys,Zs), Truth).

One append_t/4 goal can replace two prefix_of_t/3 and append/3 goals.

Because of that, the implementation of list_sublist_removed/3 gets a bit simpler than before:

list_sublist_removed([]    ,[_|_] ,[]).
list_sublist_removed([X|Xs],[L|Ls],Zs) :-
    if_(append_t([L|Ls],Xs0,[X|Xs]),
        (Zs =    Zs0 , Xs1 = Xs0),
        (Zs = [X|Zs0], Xs1 = Xs)),
    list_sublist_removed(Xs1,[L|Ls],Zs0).

Still deterministic?

?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
L = [3,4,5,6,1].

Yes! What about the following?

?-  list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],X,[3,4,5,6,1]).
X = [1,2] ;                             % succeeds with useless choice-point
false.

Nope. So there is still room for potential improvement...

Community
  • 1
  • 1
repeat
  • 19,449
  • 4
  • 51
  • 152
0

<rant>

So many years I study Prolog, still it deserves some surprises... your problem it's quite simple to solve, when you know the list library, and you have a specific mode (like the one you posted as example). But can also be also quite complex to generalize, and it's unclear to me if the approach proposed by @repeat, based on @false suggestion (if_/3 and friends) can be 'ported' to plain, old Prolog (a-la Clocksin-Mellish, just to say).

</rant>

A solution, that has been not so easy to find, based on old-school Prolog

list_sublist_removed(L, S, R) :-
    append([A, S, B], L),
    S \= [],
    list_sublist_removed(B, S, T),
    append(A, T, R),
    !
    ; L = R.

some test:

?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
L = [3, 4, 5, 6, 1].

?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],X,[3, 4, 5, 6, 1]).
X = [1, 2].

?- length(X,_), list_sublist_removed(X,[1,2],[3, 4, 5, 6, 1]).
X = [3, 4, 5, 6, 1] ;
X = [3, 4, 5, 6, 1, 2, 1] ...
CapelliC
  • 57,813
  • 4
  • 41
  • 80
  • 1
    Of course it can be ported to ISO Prolog: `dif/2` in traditional Prolog is `iso_dif/2`! – false Jul 04 '15 at 13:17
  • @false: so, coroutining it's not necessary on the bottom line ? I don't get it... I have my old interpreter, but I refrain to update it exactly because attributed variables seems too much difficult to implement. – CapelliC Jul 04 '15 at 13:19
  • 1
    [`iso_dif/2`](http://stackoverflow.com/a/20238931/772868) produces clean instantiation errors when coroutining would be necessary: Better a honest instantiation error than an incorrect failure or success. – false Jul 04 '15 at 13:22
  • @false: yes, that's clear. Well, I should test if iso_dif works in this context... – CapelliC Jul 04 '15 at 13:29
  • 1
    The point is that the determinate cases can be done without any (external) choice point creation in `(=)/3`. – false Jul 04 '15 at 13:40
  • 1
    @repeat: I'm using SWI-Prolog, and I enjoy every bit of it. Even those diverging from ISO Prolog. But I have an [old interpreter](https://github.com/CapelliC/IL) I wrote by myself, approx. C&M complete, and that would run just my version of list_sublist_removed/3... – CapelliC Jul 04 '15 at 14:25
  • @repeat: `iso_dif/2` also works for non-ground terms that are **sufficiently instantiated**: `iso_dif(A,A)` fails and `iso_dif(1+A,2+B)` succeeds. – false Jul 04 '15 at 17:03
  • @CapelliC: If you got that far to *enjoy* non-conformance, you probably also enjoy the lack of stability in the constraint system. – false Jul 04 '15 at 17:05
  • @false: too much difficult... overall, I think SWI-Prolog it's one of the best SW I **ever** tried, and it's **free** – CapelliC Jul 04 '15 at 17:11
  • @false. I didn't mean to say that `iso_dif/2` only works with ground terms. Rather, that it works with all ground terms, and that it can/should become the default way of expressing term inequality whenever one makes the implicit assumption that "all input terms are ground". – repeat Jul 04 '15 at 20:03