Given a list of elements, replicate each element N times - prolog

I have to replicate each element n times like this:
?- replicate([a,b,c],2,X). -> X = [a,a,b,b,c,c]
?- replicate([a,b,c],3,X). -> X = [a,a,a,b,b,b,c,c,c]
I have tried everything with the information I have so far, the only thing I have been able to do is to determine which is the most repeating element like this:
%List of tuples, keeps track of the number of repetitions.
modify([],X,[(X,1)]).
modify([(X,Y)|Xs],X,[(X,K)|Xs]):- K is Y+1.
modify([(Z,Y)|Xs],X,[(Z,Y)|K]):- Z =\= X, modify(Xs,X,K).
highest((X1,Y1),(_,Y2),(X1,Y1)):- Y1 >= Y2.
highest((_,Y1),(X2,Y2),(X2,Y2)):- Y2 > Y1.
maxR([X],X).
maxR([X|Xs],K):- maxR(Xs,Z),highest(X,Z,K).
rep([],R,R).
rep([X|Xs],R,R1):-modify(R,X,R2),rep(Xs,R2,R1).
maxRepeated(X,R):- rep(X,[],K),maxR(K,R).
?- maxRepeated([1,3,3,4,3,2] ,X).
X = (3, 3) .
?- maxRepeated([1,2,3,4,5,6] ,X).
X = (1, 1) .

What do you want to do ?
Take each element X of the list, get a list of N X and create a new list with the process of the rest of the list !
How to get a list of N elements X ?
replicate_one(X, N, Out) :-
length(Out, N),
maplist(=(X),Out).
Now, how to work with each element of the input, it can be easily done with the pattern [Head|Tail] :
replicate([Head|Tail], N, Out) :-
% I replicate the rest of the list
replicate(Tail, N, Tmp1),
% I replicate the first element of the list
replicate_one(Head, N, Tmp2),
% I concatenate the 2 results
append(Tmp2, Tmp1, Out).
When you work with replicate, the input looses an element each time, so you must have a process for the empty list :
replicate([], _N, []).
Now :
?- replicate([a,b,c],3,X).
X = [a, a, a, b, b, b, c, c, c].

We can split the problem into two problems:
generate a list of N elements X with a predicate we implement replicate_item/3; and
do this for every element, and concatenate the result in a prdicate named replicate/3.
#joel76 already provided a nice implementation for replicate_item/3. I will only change the order of the parameters:
replicate_item(N, X, List) :-
length(List, N),
maplist(=(X), List).
Now our replicate/3 prdicate can iterate over the list, and for each element use replicate_item/3 to generate a sublist. We can then use append/2 [swi-doc] for this:
replicate(LA, N, List) :-
maplist(replicate_item(N), LA, LL),
append(LL, List).

Related

Count specific element in list [Prolog]

I want to count a specific element in a list.
?count(3,[3,3,3],C).
count(_,[],_).
count(X,[Y|R],C):- X \= Y, count(X,R,C).
count(X,[Y|R],C):- X = Y, L is C + 1, count(X,R,L).
But I get this error and I do not see why:
Arguments are not sufficiently instantiated
In:
[1] count(3,[3,3|...],_1594)
Do you have a tipp?
The instantiation error occurs in the goal L is C + 1 as the variable C is unbound when the goal is called.
One solution is to rewrite the predicate to use an accumulator. Doing the minimal changes to your code:
count(Element, List, Count) :-
count(Element, List, 0, Count).
count(_, [], Count, Count).
count(X, [Y|R], Count0, Count) :-
X \= Y,
count(X, R, Count0, Count).
count(X, [Y|R], Count0, Count) :-
X = Y,
Count1 is Count0 + 1,
count(X, R, Count1, Count).
With your sample call:
| ?- count(3, [3,3,3], Count).
Count = 3 ? ;
no
Notice that this new definition leaves a spurious choice-point. Can you improve it so that calls (with the two first arguments ground) become deterministic? Hint: start by moving the list argument to the first position in the auxiliary count/4 predicate.

Prolog multiply elements of a list

How can I get my Prolog program to output
1*a*b*c
If I input simplify([1,a,b,c],S).?
At the moment the result would be
1*(a*(b*c)).
simplify(S,S1):-
s(S,S1).
s([H|T],C) :- T\=[],s(H,SA), s(T,SB), s0(SA*SB,C).
s([H|T],H) :- T==[].
s(A,A).
s0(A*B,S):-
S = A*B.
Thanks for your help.
The difference between 1*a*b*c and 1*(a*(b*c)) is associativity, i.e., the position of the parentheses:
?- X = 1*a*b*c, X = ((One * A) * B) * C.
X = 1*a*b*c,
One = 1,
A = a,
B = b,
C = c.
One way to do this is to "fold over the list from the left", that is to say, compute a result for the first element of the list, combine with the second element, then the third, etc. This is typically done using an accumulator argument to pass the intermediate result. In contrast, your recursion folds the list "from the right" (combining a result for the tail list with the first element, instead of the initial list with the last element).
Here's a way (very lightly tested):
list_multexp([X|Xs], Multexp) :-
list_multexp(Xs, X, Multexp). % use first element as initial acc
list_multexp([X], Acc, Acc * X).
list_multexp([X|Xs], Acc, Multexp) :-
dif(Xs, []),
list_multexp(Xs, Acc * X, Multexp).
This works for your example:
?- list_multexp([1,a,b,c], Multexp).
Multexp = 1*a*b*c ;
false.
Depends on what Prolog you're using. SWI has foldl/4 built in, which is like "reduce" in other languages. So, you could simplify your program to the following:
s(B,A,A*B).
simplify([H|L],E):- foldl(s,L,H,E).
Not sure that is what do you want but... using atom_concat/3...
simplify([H],H).
simplify([A | T], D) :-
simplify(T,B),
atom_concat(A, '*', C),
atom_concat(C, B, D).
But you have to use 1 as an "atom", so
simplify(['1',a,b,c], S),

Pairing up items of a list according to a predicate in Prolog

I have a list of atoms in Prolog, and a predicate allowed(X, Y) that checks whether a pair of items is allowed. How can I take the list and split it into pairs of items, each of which satisfies the predicate? Ideally the pairs would be generated randomly and then checked, but this is only desired.
It can be assumed that the list has an even number of items.
pair(L1,L2,(M1,M2)) :-
member(M1,L1),
member(M2,L2).
Will retrieve, on backtracking, each possible pair. For example:
?- pair([1,2,3],[a,b],X).
X = (1, a) ;
X = (1, b) ;
X = (2, a) ;
X = (2, b) ;
X = (3, a) ;
X = (3, b).
?-
May be used on a single list, too:
?- pair([1,2,3],[1,2,3],X)
However, your question is pointless since all allowed pairs are predefined by allowed/2. You may just retrieve all solutions to allowed/2. So, this is what you are looking for, I guess:
allowed_pair(List,(A,B)) :-
allowed(A,B),
member(A,List),
member(B,List).
For example, provided that allowed/2 was defined as:
allowed(1,b).
allowed(2,a).
Then:
?- allowed_pair([1,2,3,a,4,z],X).
X = (2, a) ;
false.
?-
Depending on your performance requirements and allowed/2 implementation, you may exchange order of allowed_pair/2 goals.
I ended up doing it as follows:
checked(_, _, _, N) :-
N > 5,
fail.
checked(List, ReturnedList, [X,Y], _) :-
random_member(X, List),
select(X, List, List1),
random_member(Y, List1),
allowed(X, Y),
select(Y, List1, ReturnedList).
checked(A, B, C, N) :-
checked(A, B, C, N+1).
pairMe([], []).
pairMe(List, Result) :-
checked(List, ReturnedList, [X,Y], 0),
pairMe(ReturnedList, A),
append([[X,Y]], A, Result).
It's not great, as it doesn't guarantee that it'll always find a result even if one exists, but it works for me at least.

Fact for each element of a list Prolog

I want to solve this problem in Prolog. i want give a list of natural numbers to find all the elements in the list that satisfy this condition:
All elements on the left of it are smaller than it and all the elements on the right of it are larger than it.
For example give a list [3,2,4,1,5,7,8,9,10,8] the answer would be 5,7
So far I've manage to make this function that given an element of the list it returns true or false if the element satisfises the condition described above.
check(Elem, List) :-
seperate(Elem, List, List1, List2),
lesser(Elem, List1, X1),
bigger(Elem, List2, X2),
size(X1, L1),
size(X2, L2),
size(List, L3),
match(L1, L2, L3),
Now I want to make another predicate that given a list, it does the above computations for each element of the list. Due to the fact that more than one element may satisfy it I want to create another list with all the elements that satisfy the problem.
The question would be something like ?-predicate_name([[3,2,4,1,5,7,8,9,10,8],N). and the result would be a list of elements.
Sry If I am not using the right terms of Prolog. I will describe what I want to do in sequential logic language to be more specific although it's not a good idea to think like that. If we consider a the predicate check as a function that given a list and element of the list it would return true or false whether or not the element satisfied the conditions of the problem. Now I want to parse each element of the list and for each one of it call the function check. If that would return true then I would add the element in another list a.k.a result. I want to do this in Prolog but I don't know how to iterate a list.
Here is a version using DCGs and assuming we want to compare arithmetically.
list_mid(L, M) :-
phrase(mid(M), L).
mid(M) -->
seq(Sm),
[M],
{maplist(>(M),Sm)},
seq(Gr),
{maplist(<(M),Gr)}.
seq([]) -->
[].
seq([E|Es]) -->
[E],
seq(Es).
Often it is not worth optimizing this any further. The first seq(Sm) together with the subsequent maplist/2 might be merged together. This is a bit tricky, since one has to handle separately the cases where Sm = [] and Sm = [_|_].
mid(M) -->
( [M]
| max(Mx),
[M],
{Mx < M}
),
min(M).
max(M) -->
[E],
maxi(E, M).
maxi(E, E) -->
[].
maxi(E, M) -->
[F],
{G is max(F,E)},
maxi(G, M).
min(_) -->
[].
min(M) -->
[E],
{M < E},
min(M).
I'm going to take a different approach on the problem.
We want to find all of the values that meet the criteria of being a "mid" value, which is one defined as being greater than all those before it in the list, and less than all those after.
Define a predicate mid(L, M) as meaning M is a "mid" value of L:
mid([X|T], X) :- % The first element of a list is a "mid" if...
less(X, T). % it is less than the rest of the list
mid([X|T], M) :- % M is a "mid" of [X|T] if...
mid(T, X, M). % M is a "mid" > X
% (NOTE: first element is not a "mid" by definition)
mid([X|T], LastM, X) :- % X is a "mid" > Y if...
X > LastM, % X > the last "mid"
less(X, T). % X < the rest of the list, T
mid([X|T], LastM, M) :- % Also, M is a "mid" if...
Z is max(X, LastM), % Z is the larger of X and the last "mid"
mid(T, Z, M). % M is the "mid" of T which is > Z
less(X, [Y|T]) :- % X is less than the list [Y|T] if...
X < Y, % X < Y, and
less(X, T). % X < the tail, T
less(_, []). % An element is always less than the empty list
Each query will find the next "mid":
| ?- mid([3,2,4,1,5,7,8,9,10,8], M).
M = 5 ? ;
M = 7 ? ;
no
Then they can be captured with a findall:
mids(L, Ms) :-
findall(M, mid(L, M), Ns).
| ?- mids([3,2,4,1,5,7,8,9,10,8], Ms).
Ms = [5,7]
yes
| ?- mids([2], L).
L = [2]
(1 ms) yes
This is probably not the most computationally efficient solution since it doesn't take advantage of a couple of properties of "mids". For example, "mids" will all be clustered together contiguously, so once a "mid" is found, it doesn't make sense to continue searching if an element is subsequently encountered which is not itself a "mid". If efficiency is a goal, these sorts of ideas can be worked into the logical process.
ADDENDUM
With credit to #false for reminding me about maplist, the above predicate call less(X, T) could be replaced by maplist(<(X), T) eliminating the definition of less in the above implementation.

How to take the head of one list in a list of lists and put it in another list?

I have a list of lists, which looks something like this:
[[b,c],[],[a]]
I want to write a predicate that will take a specific letter from the top of one of the lists, and put it in another list. The letter to be moved is specified beforehand. It can be placed on top of a list which is either empty, or contains a letter that is larger (b can be placed on c, but not otherwise). The letter should be removed from the original list after it has been moved.
I am having trouble telling Prolog to look for a list which starts with the specified letter, and also how to tell Prolog to put this in another list.
here is my solution, based no [nth1][1]/4 (well, you should read documentation for nth0/4, really)
/* takes a specific letter from the top of one of the lists, and puts it in another list.
The letter to be moved is specified beforehand.
It can be placed on top of a list which is either empty, or contains a letter that is larger (b can be placed on c, but not otherwise).
The letter should be removed from the original list after it has been moved.
*/
move_letter(Letter, Lists, Result) :-
% search Letter, Temp0 miss amended list [Letter|Rest]
nth1(I, Lists, [Letter|Rest], Temp0),
% reinsert Rest, Temp1 just miss Letter
nth1(I, Temp1, Rest, Temp0),
% search an appropriate place to insert Letter
nth1(J, Temp1, Candidate, Temp2),
% insertion constraints
J \= I, (Candidate = [] ; Candidate = [C|_], C #> Letter),
% update Result
nth1(J, Result, [Letter|Candidate], Temp2).
Usage examples:
?- move_letter(a,[[b,c],[],[a]],R).
R = [[a, b, c], [], []] ;
R = [[b, c], [a], []] ;
false.
?- move_letter(b,[[b,c],[],[a]],R).
R = [[c], [b], [a]] ;
false.
I followed this 'not idiomatic' route to ease the check that the insertion occurs at different place than deletion.
Below are some rules to find lists that starts with some element.
starts_with([H|T], H).
find_starts_with([],C,[]).
find_starts_with([H|T],C,[H|Y]) :- starts_with(H,C),find_starts_with(T,C,Y).
find_starts_with([H|T],C,L) :- \+ starts_with(H,C), find_starts_with(T,C,L).
Example:
| ?- find_starts_with([[1,2],[3,4],[1,5]],1,X).
X = [[1,2],[1,5]] ? ;
I like #CapelliC's concise solution. Here's an alternative solution that doesn't use the nth1 built-in. Apologies for the sucky variable names.
% move_letter : Result is L with the letter C removed from the beginning
% of one sublist and re-inserted at the beginning of another sublist
% such that the new letter is less than the original beginning letter
% of that sublist
%
move_letter(C, L, Result) :-
removed_letter(C, L, R, N), % Find & remove letter from a sublist
insert_letter(C, R, 0, N, Result). % Result is R with the letter inserted
% removed_letter : R is L with the letter C removed from the beginning of a
% sublist. The value N is the position within L that the sublist occurs
%
removed_letter(C, L, R, N) :-
removed_letter(C, L, R, 0, N).
removed_letter(C, [[C|T]|TT], [T|TT], A, A).
removed_letter(C, [L|TT], [L|TTR], A, N) :-
A1 is A + 1,
removed_letter(C, TT, TTR, A1, N).
% Insert letter in empty sublist if it's not where the letter came from;
% Insert letter at front of a sublist if it's not where the letter came from
% and the new letter is less than the current head letter;
% Or insert letter someplace later in the list of sublists
%
insert_letter(C, [[]|TT], A, N, [[C]|TT]) :-
A \== N.
insert_letter(C, [[C1|T]|TT], A, N, [[C,C1|T]|TT]) :-
A \== N,
C #< C1.
insert_letter(C, [L|TT], A, N, [L|TTR]) :-
A1 is A + 1,
insert_letter(C, TT, A1, N, TTR).
Results in:
| ?- move_letter(a, [[b,c],[],[a]], R).
R = [[a,b,c],[],[]] ? a
R = [[b,c],[a],[]]
no
| ?- move_letter(b, [[b,c],[],[a]], R).
R = [[c],[b],[a]] ? a
no
| ?- move_letter(b, [[b,c], [], [a], [b,d]], R).
R = [[c],[b],[a],[b,d]] ? a
R = [[b,c],[b],[a],[d]]
no

Resources