Related
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].
So my problem is this, i have a predicate which is repete_el(El,N,L) in which El is an element, N is the number of times is repeated and L is the list contaning that element repeated N times.
My problem is that istead of repeating the element its giving false and i dont understand why.
Example:
My output:
?- repete_el(a,3,L).
false
Correct output:
?- repete_el(a,3,L).
L = [a,a,a].
Program:
repete_el(El,0,[]) :- !.
repete_el(El,N,L) :- repete_el(El,N,L,[],N).
repete_el(El,N,L,L2) :- length(L2,C),
C =< N,
append(L2,[N],NL),
repete_el(El,N,L,NL).
By the way, i can only do this iteratively.
You can use the standard findall/3 predicate and the de facto standard between/3 predicate. For example:
| ?- findall(a, between(1,5,_), List).
List = [a, a, a, a, a]
yes
The reason this will not work is because at the moment you call repete_el/4, L2 is a free variable, so length(L2, C) will start building all sorts of lists with lengths. Then you make a recursive call on a list NL with one element extra, and you require that list to have again length C (which should again be smaller than N). But eventually C will be larger than N, and thus the predicate will fail.
You can write such predicate as:
repete_el(_, 0, []). %% (1)
repete_el(X, N, [X|T]) :- %% (2)
N > 0,
N1 is N-1,
repete_el(X, N1, T).
Here we thus say:
(1) A list where we repeat an element 0 times is an empty list; and
(2) a list where we repeat X, N times, with N greater than 0 is a list that starts with X and ends with a list where we repeat X, N-1 times.
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.
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).
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.