Prolog Create a List - list

I have to create list of n elements for example,
do_list(5,L1).
should return,
L1=[1,2,3,4,5].
This is what I have, but it's not working.
do_list(X,L1):- X1 is X-1, do_list(X1,[X1|L1]).
do_list(0,[]).

If you want to create a list of consecutive numbers from 1 to N you can use builtin predicates findall/3 and between/3 this way:
do_list(N, L):-
findall(Num, between(1, N, Num), L).
?- do_list(5,L).
L = [1, 2, 3, 4, 5].
SWI also has another builtin which does just that, numlist/3:
?- numlist(1,5,L).
L = [1, 2, 3, 4, 5].

There are three problems with your code. The first problem is that you add X1 to the list in the clause body, but you never pass the new list back towards the head of the clause. I.e., L1 is an accumulator variable, but you need a third argument that will be bound to the final list.
The second is that the second clause only matches if the input list is empty. This will never be the case, since you add X1 to the list before calling do_list/2 recursively. I.e., you don't have a recursion anchor, and the goal ?- do_list(5,L) will never return.
The third problem is that you add X1 to the list instead of X. You'd skip the largest number.
This is how it should work:
do_list(N, L) :- do_list1(N, [], L).
do_list1(0, L, L) :- !.
do_list1(N, R, L) :- N > 0, N1 is N-1, do_list1(N1, [N|R], L).

Or if you don't want to use any built-in function (like me, when I tried this as a practice and then run into this question), you can use this (working but not effective) solution:
connect([],X,X).
connect([H|T],C,[H|T2]) :- connect(T,C,T2).
revert([],[]).
revert([H|T],R) :- revert(T,Trev), connect(Trev,[H],R)
do_revlist(0,[]).
do_revlist(X,[X|L]) :- X1 is X-1, do_revlist(X1,L).
do_list(X,L2) :- do_revlist(X,L), revert(L,L2).
P.S. Works only for positive integers.

Another solution similar to that of twinterer but without cut or predefined predicates, employing an accumulator.
do_List(Max,L) :- do_ListAcc(1,Max,L). % call Accumulator
do_ListAcc(N,N,[N]). % N=:=N ends recursion
do_ListAcc(Min,Max,[Min|Succs]) :-
Next is Min + 1,
do_ListAcc(Next,Max,Succs).
This also works only for positive integers.

Related

Slicing the list in prolog between two indexes

I'm trying to slice a list, and create a result list that contains elements that fall between two indices, exclusive.
indexOf([Element|_], Element, 0). % We found the element
indexOf([_|Tail], Element, Index):-
indexOf(Tail, Element, Index1), % Check in the tail of the list
Index is Index1+1. % and increment the resulting index
less_than(0, 0).
less_than(X, N) :-
X < N.
greater_than(0, 0).
greater_than(X, N) :-
X > N.
slice([], 0, 0, []).
slice([H|T], I, N, R) :-
indexOf([H, T], H, Ind), % get the index of the H
(less_than(Ind, N),
greater_than(Ind, I), % check if that index is between I and N
slice(T, I, N, H|R); % if it is - call the slice again, but append the H to the result
slice(T, I, N, R)). % if it's not, just call the slice regularly with the rest of the items
For instance, slice([a,b,c,d], 0, 4, R) R should be [b,c].
Now this always fails but I'm not sure why.
Is it the base case, or is is the 'if-else' statement between the parenthesis?
I would like to accomplish this without using build-in predicates.
There are some fundamental flaws in your algorithm. You seem to be processing one item at a time but didn't take into account that your input list keeps shrinking, so you should decrement your starting index by one each time. Your base case probably should not check that the ending index be 0, you never modify it and you wouldn't call it with 0.
Also your indexOf/3 procedure seems to find any location where the item is found (there may be duplicate items in your list).
You are also misusing the list structure [Head|Tail] (forgot the square brackets).
You may do your slicing by using append/3:
slice(L, From, To, R):-
length(LFrom, From),
length([_|LTo], To),
append(LTo, _, L),
append(LFrom, R, LTo).
In this answer I assumed your index starts at 1.
Sample run:
?- slice([a,b,c,d,e], 1, 4, R).
R = [b, c].

Prolog - creating cumulative duplicates in a list?

I have a list L given and my task is to create cumulative duplicates, depending on how many I want.
I have the list [a,b,c,d] for example and want the first element to be duplicated 4 times (optional), then every subsequent element has to be duplicated like the previous one + 1.
Let's assume my predicate is called list_copy(L,N,R), the result with L = [a,b,c,d] and K = 2 should be:
?- list_copy([a,b,c,d],2,R).
R = [a,a,b,b,b,c,c,c,c,d,d,d,d,d]
I've managed to create a universal code for duplicating a list two times:
dupl([],[]).
dupl([O|U],[O,O|U1]) :- dupl(U,U1).
Depending on how many O's I put in my second line I get that many duplicates.
My question is, though: How can I implement a 3rd variable as a cumulative counter, in order to get the desired outcome?
When you have to "count" stuff one by one, consider using succ/2. It has the nice property that it works both ways, and fails when you call it with succ(X, 0).
So first, the predicate that does "cumulative duplicates":
cum_dup([], _, []).
cum_dup([X|Xs], N, Ys) :-
repeat_n(X, N, Ys, Back),
succ(N, N1),
cum_dup(Xs, N1, Back).
This uses the predicate repeat_n/4, which takes an elements, a non-negative integer, and repeats the element. It leaves a "hole" in the back of the list that you can fill with the rest of the result using cum_dup/3. Here is a straight-forward implementation of repeat_n/4:
repeat_n(_, 0, L, L).
repeat_n(X, N, [X|Xs], Rest) :-
succ(N0, N),
repeat_n(X, N0, Xs, Rest).
This will already give you the result you need:
?- cum_dup([a,b,c,d], 2, R).
R = [a, a, b, b, b, c, c, c, c, d, d, d, d, d] ;
false
It leaves behind a harmless choice point. There are too many ways to make a repeat_n/4 that does not leave unnecessary choice points:
use CLP(FD)
use a cut
use a conditional (if-then-else)
use a structure instead of an integer
Just one example:
repeat_n(X, N, L, Back) :-
length(Ns, N),
repeat_n_(Ns, X, L, Back).
repeat_n_([], _, L, L).
repeat_n_([_|Ns], X, [X|Xs], L) :-
repeat_n_(Ns, X, Xs, L).
Here, instead of counting with an integer, you (ab)use a list of that length.
I guess you can think about it yourself and ask another question if you really need to.
--- EDIT --- modified using succ/2, as suggested by Boris (thanks!).
I suppose you can use a helper clause. Like list_copy_h/4 in the following example
list_copy_h([], _, _, []).
list_copy_h([_ | Tin], 0, Num, Lout) :-
succ(Num, Np1),
list_copy_h(Tin, Np1, Np1, Lout).
list_copy_h([H | Tin], Count, Num, [H | Tout]) :-
succ(Cm1, Count),
list_copy_h([H | Tin], Cm1, Num, Tout).
list_copy(Lin, Num, Lout) :-
list_copy_h(Lin, Num, Num, Lout).
The idea is use a counter (the second argument) that decrease to zero. When zero, the head of the input list is discharged and the cycle begin again with the following element of the input list but increasing the starting value of the counter. The third argument is the starting value.

Writing Prolog Code which returns list of integer sums from a given number

I'm trying to write a Prolog predicate that can decomposes a given non-negative integer into every possible sum, using a DCG.
For example:
?- s(3, L, []).
L = [3] ? ;
L = [2,1] ? ;
L = [1,2] ? ;
L = [1,1,1] ? ;
false.
I started by writing a predicate which takes a number N and returns L = [1,2,3,...,N]:
mkList(N, L) :-
m(0, N, L).
m(X, X, []).
m(Y, L, [H|T]) :-
H is Y+1,
m(H, L, T).
However, I'm not sure how I can proceed.
s(Input) -->
{ mkList(Input, InputList) },
{ member(X, InputList) },
[X].
This is what I was going to use, it starts out my running through the list one by one. However, I'm not sure where I should include a rule to find the difference between X and Input.
the base case is easy:
all_sum(N) --> [N].
now, we can call recursively if we provide a M between 1 and N, and take the rest R (beware it must be > 0)
all_sum(N) --> {...},[M],all_sum(R).
please fill the dots using the hints above.
You will get
?- phrase(all_sum(3),L).
L = [3] ;
L = [1, 2] ;
L = [1, 1, 1] ;
L = [2, 1] ;
false.
The best way to proceed is to think like Prolog, that is, recursively. Yes, you've got recursion. It may even be right, but I'm not following it.
Thinking like this should work:
mkList(Number,List) :-
pick a number between 1 and number. It'll be your first addend.
subtract it from number to get the remainder.
make a recursive call to handle the remainder.
patch together List based on the first addend and the list from the recursive call.
Obviously we need to stop when Number is less than 1.
This doesn't use a DCG, but for the life of me I can't see how a DCG is relevant here.

Prolog: Finding the maximum absolute difference of two adjacent list elements

listelements([i|o],listelements) :-
u is abs((listmax)-(listmin)),
listelements(u,listelements).
listmin([min],min).
listmin([x|y],z) :-
listmin(y,h),
min(x,h,z).
min(Q,W,Q) :-
Q =< W.
min(Q,W,W) :-
W < Q.
listmax([max],max).
listmax([a|b],c) :-
listmax(b,f),
max(a,f,c).
max(M,N,M) :-
M =< N.
max(M,N,N) :-
N < M.
Arithmetic: `listmax' is not a function
This is the error. Help please!
When I ask ?- listmin([1,2,3,4,5,6],z).
it answers false instead of 1. Why?
Considering your concrete choice of arithmetic predicates,
I guess you want to support all kinds of Prolog numbers (floating point numbers, integers, and rational numbers).
If you only work with integer numbers here then make sure you are using clpfd!
In this answer we proceed step-by-step, like this:
We define an auxiliary predicate num_num_absdiff/3 which computes the absolute difference of its first two arguments.
num_num_absdiff(X,Y,D) :-
D is abs(X-Y).
To apply num_num_absdiff/3 to all adjacent items and gather the results, we use mapadj/3:
?- mapadj(num_num_absdiff,[1,2,4,8,16,100],Ds).
Ds = [1, 2, 4, 8, 84].
The maximum absolute difference is the maximum number in above list.
We employ list_maxnum/2 to get said number.
?- list_maxnum([1,2,4,8,84], Max).
Max = 84.
Let's put it all together and define maxabsdiff_of/2:
maxabsdiff_of(D,Nums) :-
mapadj(num_num_absdiff,Nums,Ds),
list_maxnum(Ds,D).
Here are some sample queries:
?- maxabsdiff_of(D,[1,2,3,4,5,6]). % as given by the OP
D = 1.
?- maxabsdiff_of(D,[1,2,3,4,5,6,8]).
D = 2.
?- maxabsdiff_of(D,[1,2,3,100,4,5,6,8]).
D = 97.
Of course, you could also use lambda together with meta-predicate foldl/4, like so:
:- use_module(library(lambda)).
maxabsdiff_of(X,[V0,V1|Vs]) :-
foldl(\E^(E0+D0)^(E+D)^(D is max(D0,abs(E-E0))), Vs, V1+abs(V1-V0),_+X).
Sample queries:
?- maxabsdiff_of(D,[1,2,3,4,5,6]).
D = 1.
?- maxabsdiff_of(D,[1,2,3,4,5,6,8]).
D = 2.
?- maxabsdiff_of(D,[1,2,3,100,4,5,6,8]).
D = 97.
Same results as before!

Easily replicate an element in Prolog :)

I am working on a longer problem that has me duplicate an element N times in list form, and I believe that using append is the right way to go for this. The tiny predicate should theoretically act like this:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
I cannot seem to find any tips for this online, the replication of a single element, but I believe we need to use append, but no recursive solution. I come from more of a Haskell background, where this problem would be much easier to perform. Can someone help get me started on this? :)
Mine so far:
repl(E, N, R) :-
N > 0, append([E], [], R), writeln(R), repl(E, N-1, R), fail.
Which gives me:
?- repl(x,5,L).
[x]
[x]
[x]
[x]
[x]
false.
Close but not quite!
A recursive approach would be straight-forward and would work. I recommend figuring that one out. But here's a fun alternative:
repl(X, N, L) :-
length(L, N),
maplist(=(X), L).
If N is instantiated, then length(L, N) will generate a list of length N of just "blanks" (don't care terms). Then maplist(=(X), L) will unify each element of L with the variable X.
This gives a nice, relational approach and yields sensible results in the general case:
| ?- repl(X, N, L).
L = []
N = 0 ? ;
L = [X]
N = 1 ? ;
L = [X,X]
N = 2 ? ;
| ?- repl(X, N, [x,x,x]).
N = 3
X = x
yes
...
To figure out a recursive case, think about what your base case looks like (it would be repl with a count of 0 - what does the list look like then?). In the recursive case, think in terms of:
repl(X, N, [X|T]) :- ...
Meaning: The list [X|T] is the element X repeated N times if.... Figure out if what? If your base case is length 0, then your recursion is probably going to describe the repl of a list of length N in terms of the repl of a list of length N-1. Don't forget in this recursive rule to ensure N > 0 to avoid infinite recursion on backtracking. If you don't need the predicate to be purely relational and assume N is instantiated, then it can be fairly simple.
If you make a simple recursive version, you can "wrap" it in this predicate to make it work with variable N:
repl(X, N, L) :-
length(L, N),
simple_recursive_repl(X, N, L).
...
Because length/2 is relational, it is much more useful than just providing the length o a given list. When N and L are not instantiated, it becomes a generator of variable lists, starting at length 0. Type, length(L, N). at the Prolog prompt and see what happens.
Determinism
You give the following example of the predicate you envision:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
Notice that the ; is not very productive here. If you want to repeat x 5 times, then this can be done in exactly one way. I would therefore specify this predicate as deterministic not nondeterministic as you are doing.
Repeating list
Your code is actually quite far off a working solution, despite the output looking quite close in spirit to the envisioned result. You try to define the base case and the recursive case at the same time, which will not work.
Here is a simple (but less fun than #lurker gave :-)) implementation of the base and recursive case:
repeating_list(_, 0, []):- !.
repeating_list(H, Reps1, [H|T]):-
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
In a sense #lurker's implementation is simpler, and it is surely shorter.
Some extensions
In real-world/production code you would like to catch type errors and treat different instantiations with the same predicate. The second clause checks whether a given list consists of repeating elements (and if so, which one and how many occurrences there are).
%! repeating_list(+Term:term, +Repeats:integer, -List:list(term)) is det.
%! repeating_list(?Term:term, ?Repeats:integer, +List:list(term)) is det.
repeating_list(_, 0, []):- !.
% The term and number of repetitions are known given the list.
repeating_list(H, Reps, L):-
nonvar(L), !,
L = [H|T],
forall(
member(X, T),
% ==/2, since `[a,X]` does not contain 2 repetitions of `a`.
X == H
),
length([H|T], Reps).
% Repetitions is given, then we generate the list.
repeating_list(H, Reps1, [H|T]):-
must_be(nonneg, Reps1), !,
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
% Repetitions is not `nonneg`.
repeating_list(_, Reps, _):-
domain_error(nonneg, Reps).
Notice that I throw a domain error in case the number of repetitions is negative. This uses library error in SWI-Prolog. If your Prolog does not support this feature, then you may leave the last clause out.
PS: Comparison to Haskell
The combination of your statement that you do not know how to solve this problem in Prolog and your statement that this problem can be solved much easier in Haskell seems a little strange to me. I think you can only compare the difficulty of two implementations once you know how both of them look like.
I do prefer findall/3 to build lists, and between/3 to work with ranges:
repl(E, N, L) :- findall(E, between(1, N, _), L).