How can I delete every occurrence of a sublist from a list in prolog?

前端 未结 3 1406
鱼传尺愫
鱼传尺愫 2020-12-22 01:07

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_el         


        
相关标签:
3条回答
  • 2020-12-22 01:22

    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...

    0 讨论(0)
  • 2020-12-22 01:27

    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] ? ...
      
    0 讨论(0)
  • 2020-12-22 01:29

    <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] ...
    
    0 讨论(0)
提交回复
热议问题