Improving list generation over a range in Prolog - list

I'm fairly new to Prolog, and falling in love with it more and more. I'm wondering if this implementation can be further generalized or improved upon, and whether it is idiomatic Prolog code?
%% range/2
range(End, List) :-
End > 0, !,
range_ascend(0, End, 1, List).
range(End, List) :-
End < 0,
range_descend(0, End, 1, List).
%% range/3
range(Start, End, List) :-
((Start =< End) ->
(range_ascend(Start, End, 1, List))
;
(range_descend(Start, End, 1, List))).
%% range/4 (+Start, +End, +Step, -List)
range(Start, End, Step, List) :-
((Start =< End) ->
(range_ascend(Start, End, Step, List))
;
(range_descend(Start, End, Step, List))).
range_descend(Start, End, _, []) :-
End >= Start, !.
range_descend(Start, End, Step, [Start|Rest]) :-
Next is Start - Step,
range_descend(Next, End, Step, Rest).
range_ascend(Start, End, _, []) :-
Start >= End, !.
range_ascend(Start, End, Step, [Start|Rest]) :-
Next is Start + Step,
range_ascend(Next, End, Step, Rest).

One of the main problems of your implementation is that it only works "one way", that is that your code works well when End is set to a certain value, but does not work when it is a variable:
?- range(X,[0,1,2,3]).
ERROR: >/2: Arguments are not sufficiently instantiated
Maybe you do not need such behavior to work but in Prolog it is usually both desirable and elegant that your predicate acts as a true relation, that works in multiple different ways.
However, implementing predicates as such is often more difficult than implementing them to work in a functional way, especially if you're a beginner to Prolog.
I am not going to go into details as to how to improve your code specifically (I think this is more of a question for the Code Review SE site). I am however presenting below a range predicate with better behavior than yours:
range(I, S, [I|R]) :-
I #=< S,
if_(I = S,
R = [],
( J #= I + 1,
range(J, S, R)
)
).
This predicate requires if_/3 and (=)/3 from library(reif), as well as library(clpfd) (which you can include in your program with :- use_module(library(clpfd)).).
As you can see it is much shorter than your implementation, but also works well in multiple different scenarios:
?- range(0,5,L). % Same behavior as your predicate
L = [0, 1, 2, 3, 4, 5].
?- range(-5,0,L). % Different behavior from your predicate, but logically sounder
L = [-5, -4, -3, -2, -1, 0]
?- range(1,S,[1,2,3,4,5]). % Finding the max of the range
S = 5 ;
false.
?- range(I,S,[1,2,3,4,5]). % Finding both the min and max of the range
I = 1,
S = 5 ;
false.
?- range(I,S,[1,2,X,Y,5]). % With variables in the range
I = 1,
S = 5,
X = 3,
Y = 4 ;
false.
?- range(1,S,L). % Generating ranges
S = 1,
L = [1] ;
S = 2,
L = [1, 2] ;
S = 3,
L = [1, 2, 3] ;
S = 4,
L = [1, 2, 3, 4] ;
…
?- range(I,1,L). % Generating ranges
I = 1,
L = [1] ;
I = 0,
L = [0, 1] ;
I = -1,
L = [-1, 0, 1] ;
I = -2,
L = [-2, -1, 0, 1] ;
…
?- range(I,S,L). % Generating all possible ranges
I = S,
L = [S],
S in inf..sup ;
L = [I, S],
I+1#=S,
S#>=I,
dif(I, S) ;
L = [I, _G6396, S],
I+1#=_G6396,
S#>=I,
dif(I, S),
_G6396+1#=S,
S#>=_G6396,
dif(_G6396, S) ;
…
I think you can see how many behaviors are displayed here, and how useful it can be to have access to all of them with only one predicate.
This predicate uses Constraint Logic Programming (the clpfd library). CLP allows to write relations between integers (which are the #=< and #= that you see in the code, as opposed to the classic =< and is that you use in low-level arithmetic). This is what does most of the heavy-lifting for us and allows to write short, declarative code about integers (which you cannot do easily with is).
I recommend you to read The Power of Prolog's arithmetic chapter by Markus Triska to learn about CLP arithmetic in Prolog, which is definitely a subject you need to learn if you intend to use Prolog seriously (as I hope I have illustrated in this answer).

Related

A length/2 predicate that works with constraints [duplicate]

Here is the problem:
$ swipl
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.6-5-g5aeabd5)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.
For help, use ?- help(Topic). or ?- apropos(Word).
?- use_module(library(clpfd)).
true.
?- N in 1..3, length(L, N).
N = 1,
L = [_G1580] ;
N = 2,
L = [_G1580, _G1583] ;
N = 3,
L = [_G1580, _G1583, _G1586] ;
ERROR: Out of global stack % after a while
(I can switch the order of the subqueries, the result is the same).
I guess I need to label N before I can use it, but I wonder what the problem is? I have not managed to choke up length/2 before.
What's probably more useful than a slightly less nondeterministic length/2 is a proper list-length constraint. You can find an ECLiPSe implementation of it here, called len/2. With this you get the following behaviour:
?- N :: 1..3, len(Xs, N).
N = N{1 .. 3}
Xs = [_431|_482] % note it must contain at least one element!
There is 1 delayed goal.
Yes (0.00s cpu)
You can then enumerate the valid lists either by enumerating N:
?- N :: 1..3, len(Xs, N), indomain(N).
N = 1
Xs = [_478]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_478, _557]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_478, _557, _561]
Yes (0.02s cpu, solution 3)
or by generating lists with good old standard length/2:
?- N :: 1..3, len(Xs, N), length(Xs, _).
N = 1
Xs = [_488]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_488, _555]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_488, _555, _636]
Yes (0.02s cpu, solution 3)
Let's start with the most obvious one. If you switch the goals, you have:
?- length(L, N), N in 1..3.
which has the same termination properties as:
?- length(L, N), false, N in 1..3.
So obviously, this must not terminate with Prolog's execution mechanism.
However, if you put N in 1..3 in front, this might affect termination. To do so, it must be possible with finite means to prove that there is no N from 4 on. How can you prove this in a system without constraints - that is, only with syntactic unification present? Well, you can't. And length/2 is commonly defined just without constraints present.
With library(clpfd) things are trivial, for N #>= 4, N in 1..3 simply fails1. Note also that library(clpfd) does not collaborate much with library(clpq) which might be an interesting candidate, too.
As a consequence you would need to define your own length — for each constraint package you are interested in. That's a bit of a pity, but currently there is no generic way to do so in sight. ((That is, if you are interested and think a bit about it, you might come up with a nice API that every constraint system should adhere to. Alas, this will take some more decades, I suspect. Currently, there is much too much divergence.))
So here is a first naive way for fd_length/2:
fd_length([], N) :-
N #= 0.
fd_length([_|L], N0) :-
N0 #>= 1,
N1 #= N0-1,
fd_length(L, N1).
OK, this could be optimized to avoid the superfluous choicepoint. But there is a more fundamental problem: If you are determining the length of a list of length N, this will create N constraint variables! But we do need only one.
fd_length(L, N) :-
N #>= 0,
fd_length(L, N, 0).
fd_length([], N, N0) :-
N #= N0.
fd_length([_|L], N, N0) :-
N1 is N0+1,
N #>= N1,
fd_length(L, N, N1).
Again, this is not perfect for so many reasons: It could use Brent's algorithm like current systems do ; and combine it with all the fd properties. Also, arithmetic expressions are probably not a good idea to permit ; but I would have to wait for (#)/1 in SWI...
1: Strictly speaking, this "simply fails" only for SICStus, SWI, and YAP. For in those systems, there is no accidental failure due to exhaustion of the current representation. That is, their failure can always be taken as an honest no.
How about the following baroque work-around based on clpfd and meta-predicate tcount/3?
:- use_module([library(clpfd), library(lambda)]).
list_FDlen(Xs, N) :-
tcount(\_^ =(true), Xs, N).
Let's query!
?- N in 1..3, list_FDlen(Xs, N).
N = 1, Xs = [_A]
; N = 2, Xs = [_A,_B]
; N = 3, Xs = [_A,_B,_C]
; false. % terminates universally
?- N in inf..2, list_FDlen(Xs, N).
N = 0, Xs = []
; N = 1, Xs = [_A]
; N = 2, Xs = [_A,_B]
; false. % terminates universally, too
What about this particular query?
?- N in 2..sup, list_FDlen(Xs, N).
N = 2, Xs = [_A,_B]
; N = 3, Xs = [_A,_B,_C]
; N = 4, Xs = [_A,_B,_C,_D]
... % does not terminate (as expected)
We present a clpfd-ish variant of
length/2 that's tailored to #mat's clpfd implementation.
:- use_module(library(clpfd)).
:- use_module(library(dialect/sicstus)).
:- multifile clpfd:run_propagator/2.
The "exported" predicate lazy_len/2 is defined like this:
lazy_len(Es, N) :-
N in 0..sup, % lengths are always non-negative integers
lazylist_acc_len(Es, 0, N),
create_mutable(Es+0, State),
clpfd:make_propagator(list_FD_size(State,N), Propagator),
clpfd:init_propagator(N, Propagator),
clpfd:trigger_once(Propagator).
The global constraint handler list_FD_size/3 incrementally modifies its internal state as constraint propagation occurs. All modifications are trailed and are un-done upon backtracking.
clpfd:run_propagator(list_FD_size(State,N), _MState) :-
get_mutable(Es0+Min0, State),
fd_inf(N, Min),
Diff is Min - Min0,
length(Delta, Diff),
append(Delta, Es, Es0),
( integer(N)
-> Es = []
; Delta = []
-> true % unchanged
; update_mutable(Es+Min, State)
).
lazy_len/2 tackles the problem from two sides; the clpfd constraint part of it was shown above. The tree side uses prolog-coroutining to walk down the list as far as the partial instantiation allows1:
lazylist_acc_len(_, _, N) :-
integer(N),
!.
lazylist_acc_len(Es, N0, N) :-
var(Es),
!,
when((nonvar(N);nonvar(Es)), lazylist_acc_len(Es,N0,N)).
lazylist_acc_len([], N, N).
lazylist_acc_len([_|Es], N0, N) :-
N1 is N0+1,
N in N1..sup,
lazylist_acc_len(Es, N1, N).
Sample queries:
?- lazy_len(Xs, N).
when((nonvar(N);nonvar(Xs)), lazylist_acc_len(Xs,0,N)),
N in 0..sup,
list_FD_size(Xs+0, N).
?- lazy_len(Xs, 3).
Xs = [_A,_B,_C].
?- lazy_len([_,_], L).
L = 2.
?- lazy_len(Xs, L), L #> 0.
Xs = [_A|_B],
when((nonvar(L);nonvar(_B)), lazylist_acc_len(_B,1,L)),
L in 1..sup,
list_FD_size(_B+1, L).
?- lazy_len(Xs, L), L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).
?- lazy_len(Xs, L), L #> 0, L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).
And, at long last, one more query... well, actually two more: one going up—the other going down.
?- L in 1..4, lazy_len(Xs, L), labeling([up], [L]).
L = 1, Xs = [_A]
; L = 2, Xs = [_A,_B]
; L = 3, Xs = [_A,_B,_C]
; L = 4, Xs = [_A,_B,_C,_D].
?- L in 1..4, lazy_len(Xs, L), labeling([down], [L]).
L = 4, Xs = [_A,_B,_C,_D]
; L = 3, Xs = [_A,_B,_C]
; L = 2, Xs = [_A,_B]
; L = 1, Xs = [_A].
Footnote 1:
Here, we focus on preserving determinism (avoid the creation of choice-points) by using delayed goals.

Remove leading zeros in list in Prolog

I have a list with an unknown number of zeros at the beginning of it, for example [0, 0, 0, 1, 2, 0, 3]. I need this list to be stripped of leading zeros, so that it would look like [1, 2, 0 , 3].
Here's what I have:
lead([Head | _], _) :- Head =\= 0.
lead([0 | Tail], _) :-
lead(Tail, Tail).
The output of which is simply True. Reading the trace shows that it is running until it has a list with no leading zeros, but then the answer doesn't propagate back up the stack. I'm pretty new to Prolog, so I can't figure out how to make it do that.
Here is a solution that works in all directions:
lead([],[]).
lead([H|T],[H|T]) :-
dif(H,0).
lead([0|T],T2) :-
lead(T,T2).
Some queries:
?- lead([0,0,0,1,2,0,3], L).
L = [1, 2, 0, 3] ;
false.
?- lead(L, []).
L = [] ;
L = [0] ;
L = [0, 0] ;
L = [0, 0, 0] ;
...
?- lead(L0, L).
L0 = L, L = [] ;
L0 = L, L = [_G489|_G490],
dif(_G489, 0) ;
L0 = [0],
L = [] ;
L0 = [0, _G495|_G496],
L = [_G495|_G496],
dif(_G495, 0) ;
L0 = [0, 0],
L = [] ;
L0 = [0, 0, _G501|_G502],
L = [_G501|_G502],
dif(_G501, 0) ;
L0 = [0, 0, 0],
L = [] ;
...
EDIT This predicate actually doesn't work for e.g. lead(L0, [0,1,2]).
With library(reif):
:- use_module(reif).
remove_leading_zeros([], []).
remove_leading_zeros([H|T], Rest) :-
if_( H = 0,
remove_leading_zeros(T, Rest),
Rest = [H|T]).
Then:
?- remove_leading_zeros([0,0,0,1,2,0,3], R).
R = [1, 2, 0, 3].
?- remove_leading_zeros([2,0,3], R).
R = [2, 0, 3].
?- remove_leading_zeros(L, R).
L = R, R = [] ;
L = [0],
R = [] ;
L = [0, 0],
R = [] ;
L = [0, 0, 0],
R = [] . % and so on
Here is a solution that actually works for all possible inputs and doesn't leave unnecessary choice points:
lead(L0, L) :-
( nonvar(L),
L = [H|_] ->
dif(H,0)
;
true
),
lead_(L0, L).
lead_([], []).
lead_([H|T], L) :-
if_(H \= 0,
L = [H|T],
lead_(T,L)).
The initial check for nonvar(L) is the only solution I have been able to come up with that would prevent problems with e.g. lead(L0, [0,1,2,3]), while retaining the behavior of the predicate in all other situations.
This uses if_/3, part of library(reif)
if_(If_1, Then_0, Else_0) :-
call(If_1, T),
( T == true -> Then_0
; T == false -> Else_0
; nonvar(T) -> throw(error(type_error(boolean,T),
type_error(call(If_1,T),2,boolean,T)))
; throw(error(instantiation_error,instantiation_error(call(If_1,T),2)))
).
This also uses (\=)/3, that I came up with by simple modification of (=)/3 in library(reif).
\=(X, Y, T) :-
( X \= Y -> T = true
; X == Y -> T = false
; T = true, dif(X, Y)
; T = false,
X = Y
).
Some queries
?- lead([0,0,0,1,2,0,3],L). % No choice point
L = [1, 2, 0, 3].
?- lead([1,2,0,3],L).
L = [1, 2, 0, 3].
?- lead([0,0,0,0],L).
L = [].
?- lead([],L).
L = [].
?- lead(L0,[0,1,2,0,3]). % Correctly fails
false.
?- lead(L0,[1,2,0,3]).
L0 = [1, 2, 0, 3] ;
L0 = [0, 1, 2, 0, 3] ;
L0 = [0, 0, 1, 2, 0, 3] ;
…
?- lead(L0,L). % Exhaustively enumerates all cases:
L0 = L, L = [] ; % - LO empty
L0 = L, L = [_G2611|_G2612], % - L0 contains no leading 0
dif(_G2611, 0) ;
L0 = [0], % - L0 = [0]
L = [] ;
L0 = [0, _G2629|_G2630], % - L0 contains one leading 0
L = [_G2629|_G2630],
dif(_G2629, 0) ;
L0 = [0, 0], % - L0 = [0, 0]
L = [] ;
L0 = [0, 0, _G2647|_G2648], % - L0 contains two leading 0s
L = [_G2647|_G2648],
dif(_G2647, 0) ;
… % etc.
Here is a solution that doesn't generate any choice points. Its
using freeze/2, in a way that is not anticipated by dif/2. But using
freeze/2 here is quite appropriate, since one rule of thumb for freeze/2
is as follows:
Rule of Thumb for freeze/2: Use freeze/2 where the predicate would
generate uninstantiated solutions and a lot of choice points. The hope
is that a subsequent goal will specify the solution more, and the
freeze/2 will be woken up. Unfortunately doesn't work with CLP(FD) or
dif/2, since freeze/2 does not react to refinements implied by CLP(FD)
or dif/2, only unification will wake it up.
The code is thus:
lead(X, Y) :- var(X), !, freeze(X, lead(X,Y)).
lead([X|Y], Z) :- var(X), !, freeze(X, lead([X|Y],Z)).
lead([0|X], Y) :- !, lead(X, Y).
lead(X, X).
Here are some sample runs (SWI-Prolog without some import, Jekejeke Prolog use Minlog Extension and ?- use_module(library(term/suspend))):
?- lead([0,0,0,1,2,3], X).
X = [1, 2, 3].
?- lead([0,0|X], Y).
freeze(X, lead(X, Y)).
?- lead([0,0|X], Y), X = [0,1,2,3].
X = [0, 1, 2, 3],
Y = [1, 2, 3].
?- lead([Z,0|X], Y), X = [0,1,2,3].
X = [0, 1, 2, 3],
freeze(Z, lead([Z, 0, 0, 1, 2, 3], Y)).
?- lead([Z,0|X], Y), X = [0,1,2,3], Z = 0.
Z = 0,
X = [0, 1, 2, 3],
Y = [1, 2, 3].
In the above lead/2 implemetation only the first argument is handled. To handle multiple arguments simultaneously the predicate when/2 can be used. But for simplicity this is not shown here.
Also when using suspended goals, one might need a labeling like predicate at the end, since suspended goals cannot detect inconsistency among them.
The problem in your code is that the second parameter, your output, is specified as _, so your predicate is true for any output. What you want is a predicate that is true if and only if it is the input minus leading zeroes.
lead([], []).
lead([0 | Tail], Tail2) :- !, lead(Tail, Tail2).
lead([Head | Tail], [Head | Tail]) :- Head =\= 0.
The ! in the first line is optional. It prunes the search tree so Prolog does not consider the second line (which would fail) if the first line matches.
Here's how I'd phrase it. First, establish constraints: either X or Y must be bound to a list. Anything else fails.
If X is bound, we don't care about Y: it can be bound or unbound. We just strip any leading zeros from X and unify the results with Y. This path has a single possible solution.
If X is unbound and Y is bound, we shift into generative mode. This path has an infinite number of possible solutions.
The code:
strip_leading_zeros(X,Y) :- listish(X), !, rmv0( X , Y ) .
strip_leading_zeros(X,Y) :- listish(Y), !, add0( Y , X ) .
rmv0( [] , [] ) .
rmv0( [D|Ds] , R ) :- D \= 0 -> R = [D|Ds] ; rmv0(Ds,R) .
add0( X , X ) .
add0( X , Y ) :- add0([0|X],Y ) .
listish/1 is a simple shallow test for listish-ness. Use is_list/1 if you want to be pedantic about things.
listish( L ) :- var(L), !, fail.
listish( [] ) .
listish( [_|_] ) .
Edited to note: is_list/1 traverses the entire list to ensure that it is testing is a properly constructed list, that is, a ./2 term, whose right-hand child is itself either another ./2 term or the atom [] (which denotes the empty list). If the list is long, this can be an expensive operation.
So, something like [a,b,c] is a proper list and is actually this term: .(a,.(b,.(c,[]))). Something like [a,b|32] is not a proper list: it is the term .(a,.(b,32)).

Sum up a list using Prolog

I want to sum all list elements greater than some given number. Here's the description:
sumup(L, N, GREATN, GEN) sums up the members of list L which are greater than GREATN to a variable N and puts these members into the list GEN.
Sample query:
?- sumup([8, 6, 10, 3, 9, 12], N, 7, GEN).
GEN = [8, 10, 9, 12], % expected answer
N = 39. % 8+10+9+12 = 39
Following is my code:
sum_list([], 0).
sum_list([H|T], Sum) :-
H > 3,
sum_list(T, Rest),
Sum is H + Rest.
sum_list([H|T], Sum) :-
H < 3,
write('').
I've tried the recursive way but I failed. How can I fix it?
Looking at your question and your code, I noticed a few things:
While you speak of "numbers" several times, your samples are integer-only. May we neglect non-integer numbers (float, rational) and handle integers only? I guess so.
There is an auto-loaded SWI-Prolog library(lists) predicate sum_list/2.
Calling your predicate sum_list/2 is an unfortunate choice.
Let's pick another name!
Your definition of sum_list/2 comprises three clauses:
sum_list([], 0).
Okay!
sum_list([H|T], Sum) :- H > 3, sum_list(T, Rest), Sum is H + Rest.
Notice H > 3? Why hardcode the constant integer 3?
sum_list([H|T], Sum) :- H < 3, write('').
That clause is not recursive. We need to see all list elements to calculate the sum, not stop at the first list element H that fulfills H < 3!
What's the use of write('')? I don't see any.
What good is the goal H < 3? Like above, why hardcode the integer 3?
Clause #2 covers H > 3. Clause #3 covers H < 3. What about H = 3?
In this answer we use clpfd, which is present in swi-prolog.
Here's a straight-forward definition of sumup/4 based on clpfd. While it could be improved in several ways (better determinism, accumulator-style, possibly some clever redundant constraints could also help), but for the time being it's a nice first shot:
:- use_module(library(clpfd)).
sumup([], 0, _, []).
sumup([Z|Zs], S0, P, [Z|Xs]) :-
Z #> P,
S0 #= S+Z,
sumup(Zs, S, P, Xs).
sumup([Z|Zs], S, P, Xs) :-
Z #=< P,
sumup(Zs, S, P, Xs).
Sample query as given by the OP:
?- sumup([8,6,10,3,9,12], N, 7, GEN).
N = 39, GEN = [8,10,9,12] % expected answer
; false. % leftover useless choicepoint
No need to write recursive code! Just use tfilter/3, (#<)/3, and clpfd:sum/3 like this:
:- use_module(library(clpfd)).
sumup(Zs, S, P, Xs) :-
tfilter(#<(P), Zs, Xs),
sum(Xs, #=, S).
Sample query:
?- sumup([8,6,10,3,9,12], S, 7, Xs).
S = 39, Xs = [8,10,9,12]. % expected result
Note that above query succeeds deterministically—a clear improvement over this previous answer!
Bonus! As the implementation of sumup/4 is monotonic, we know that the solution of above query is also part of the solution set of every generalization of the query. Look for yourself!
?- sumup([8,6,10,3,9,12], S, E, Xs).
S = 48, E in inf..2 , Xs = [8,6,10,3,9,12]
; S = 45, E in 3..5 , Xs = [8,6,10, 9,12]
; S = 39, E in 6..7 , Xs = [8, 10, 9,12] % <==== solution of above query
; S = 31, E in 8..8 , Xs = [10, 9,12]
; S = 22, E in 9..9 , Xs = [10, 12]
; S = 12, E in 10..11 , Xs = [12]
; S = 0, E in 12..sup, Xs = []
; false.
In SWI-Prolog you can use a fold and simply query:
L=[8, 6, 10, 3, 9, 12], include(<(7),L,Gen), foldl(plus,Gen,0,N).
so that sumup would be written as
sumup(L,N,GreatN,Gen) :-
include(<(GreatN),L,Gen),
foldl(plus,Gen,0,N).
plus/3 is an arithmetic predicate that works well in our case.

insert Element at specific position

I started Prolog (just for my own) and i am struggling with recursion.
I want a "method", that inserts an element at a specific position within a list.
What i tried so far is :
insertAt(Element,Position,List,ResultList)
insertAt(Element,0,L,[Element|L]).
insertAt(Element,Pos,[E|L],ZL):-
Pos1 is Pos-1,
insertAt(Element,Pos1,L,ZL),
append(E,ZL1,ZL).
I find i quite complicated, since i cant understand how the recursion exactly works...
Maybe someone can help me out.
There are several features of your code that make it hard to understand for beginners. In particular, the use of moded, low-level arithmetic is a severe impediment when interacting with the program in a playful (and in fact also in a serious) way.
For example, to understand a relation, it is useful to start with the most general query. This only asks "Is there any solution at all, and if so, what does a solution look like?". In your specific example, the most general query looks like:
?- insertAt(E, Pos, Ls0, Ls).
and this almost immediately yields an instantiation error due to the non-declarative arithmetic predicates you are using:
?- insertAt(E, Pos, Ls0, Ls).
Pos = 0,
Ls = [E|Ls0] ;
ERROR: insertAt/4: Arguments are not sufficiently instantiated
In addition, you are impeding a nice declarative reading by using an imperative name ("insert..."). This makes it unnecessarily hard to develop a feeling for relational programming.
Therefore, I recommend you: (1) Use a more declarative predicate name, and (2) use a symbolic representation of natural numbers that makes the predicate easier to understand and more general.
I will use the number 0 to represent zero, and the term s(X) to represent the successor of the number X. See successor-arithmetics for more information about this representation.
With these changes, the code becomes:
element_at(E, 0, [_|Ls], [E|Ls]).
element_at(E, s(X), [L|Ls0], [L|Ls]) :-
element_at(E, X, Ls0, Ls).
To understand this program, we read it declaratively: The first clause is true if the position is 0, and the head of the final list is E, and the tail ... etc. The second clause is true if element_at(E, X, Ls0, Ls) holds, and the head of ... etc.
Nicely, the most general query now works much better:
?- element_at(E, Pos, Ls0, Ls).
Pos = 0,
Ls0 = [_G1071|_G1072],
Ls = [E|_G1072] ;
Pos = s(0),
Ls0 = [_G1073, _G1079|_G1080],
Ls = [_G1073, E|_G1080] ;
Pos = s(s(0)),
Ls0 = [_G1073, _G1081, _G1087|_G1088],
Ls = [_G1073, _G1081, E|_G1088] .
Notice though that there is something unfair going on here: Where are answers for remaining positions? For fairer enumeration, we use length/2, stating in advance the length of the lists we are considering one after another:
?- length(Ls0, _), element_at(E, Pos, Ls0, Ls).
Ls0 = [_G1124],
Pos = 0,
Ls = [E] ;
Ls0 = [_G1124, _G1127],
Pos = 0,
Ls = [E, _G1127] ;
Ls0 = [_G1124, _G1127],
Pos = s(0),
Ls = [_G1124, E] .
And now it is clearer how the different arguments interact, because you already see various examples of terms that are described by your predicate.
In fact, to reduce the number of arguments and variable names we need to keep track of, I often use DCG notation when describing lists, and I would like to show you this alternative version too:
element_at(Element, 0, [_|Ls]) -->
[Element],
list(Ls).
element_at(Element, s(X), [L|Ls]) -->
[L],
element_at(Element, X, Ls).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
?- length(Ls0, _), phrase(element_at(E, Pos, Ls0), Ls).
Ls0 = [_G1148],
Pos = 0,
Ls = [E] ;
Ls0 = [_G1148, _G1151],
Pos = 0,
Ls = [E, _G1151] ;
Ls0 = [_G1148, _G1151],
Pos = s(0),
Ls = [_G1148, E] .
Once you read up on dcg notation, this version will become clear to you.
At last, you may say "Well, that's nice, but s(X) notation still seems quite strange", and you may want to use the more widely used Hindu-Arabic notation for integers in your programs.
For this, we can simply take either version from above and replace s(X) notation by declarative integer arithmetic with CLP(FD) constraints. For example, with the first version:
:- use_module(library(clpfd)).
element_at(E, 0, [_|Ls], [E|Ls]).
element_at(E, X, [L|Ls0], [L|Ls]) :-
X #> 0,
X #= X0 + 1,
element_at(E, X0, Ls0, Ls).
This also works in all directions, exactly as we expect from a nicely declarative and general predicate:
?- length(Ls0, _), element_at(E, Pos, Ls0, Ls).
Ls0 = [_G2095],
Pos = 0,
Ls = [E] ;
Ls0 = [_G2095, _G2098],
Pos = 0,
Ls = [E, _G2098] ;
Ls0 = [_G2095, _G2098],
Pos = 1,
Ls = [_G2095, E] .
Please see clpfd for more information, and I hope this post encourages you to think more relationally about your Prolog code, try it in all directions, and read it declaratively. (What is being described?)
Let same_length/2, append/3, and length/2 take care of recursion!
insertAt(E,N,Xs,Ys) :-
same_length([E|Xs],Ys),
append(Before,Xs0,Xs),
length(Before,N),
append(Before,[E|Xs0],Ys).
Sample query:
?- insertAt(X, N, [a,b,c,d,e], Ys).
( N = 0, Ys = [X,a,b,c,d,e]
; N = 1, Ys = [a,X,b,c,d,e]
; N = 2, Ys = [a,b,X,c,d,e]
; N = 3, Ys = [a,b,c,X,d,e]
; N = 4, Ys = [a,b,c,d,X,e]
; N = 5, Ys = [a,b,c,d,e,X]
; false
).
A Prolog feature is pattern matching, that is rule selection based on predicate arguments. Such feature it's key to Prolog notation, allowing for compact description of relation, notably for on recursive terms, like lists. Note, lists are just 'syntactic sugar' for recursive terms, with a conventional functor (term' name, in every day parlance).
insertAt(Element,0,L,[Element|L]). % ok
insertAt(Element,Pos,[E|L],[E|ZL]):- % you forgot to cons back E
Pos1 is Pos-1,
insertAt(Element,Pos1,L,ZL). % done, append is useless
%append(E,ZL1,ZL).
SWI-Prolog has nth1/4 and nth0/4, that can perform insertion:
?- nth0(1,L,x,[1,2,3]).
L = [1, x, 2, 3].

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

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