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
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...
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
:
First (test), we check if [L|Ls]
is a prefix of [X|Xs]
.
If it is present (case 1), we strip it off [X|Xs]
yielding Xs0
and add nothing to Zs
.
If it is absent (case 2), we strip X
off [X|Xs]
and add X
to Zs
.
We recurse on the rest of [X|Xs]
until no more items are left to process.
Onwards to some queries!
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
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
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] ? ...
<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] ...