Custom reverse of a list in Prolog - list

I am trying to write a predicate that given the following list in Prolog:
[[1,a,b],[2,c,d],[[3,e,f],[4,g,h],[5,i,j]],[6,k,l]]
will produce the following list:
[[6,k,l],[[5,i,j],[4,g,h],[3,e,f]],[2,c,d],[1,a,b]]
As you can see I would like to preserve the order of the elements at the lowest level, to produce elements in the order 1, a, b and NOT b, a, 1.
I would also like to preserve the depth of the lists, that is, lists that are originally nested are returned as such, but in reverse order.
I have managed to achieve the desired order with the following code, but the depth is lost, i.e. lists are no longer nested correctly:
accRev([F,S,T],A,R) :- F \= [_|_], S \= [_|_], T \= [_|_],
accRev([],[[F,S,T]|A],R).
accRev([H|T],A,R) :- accRev(H,[],R1), accRev(T,[],R2), append(R2,R1,R).
accRev([],A,A).
rev(A,B) :- accRev(A,[],B).
I would appreciate help in correcting the code to preserve the correct nesting of lists. Thanks!

Two suggestions. First, here's one (rev_lists/2) which uses a bunch of SWI-PROLOG built-ins:
rev_lists(L, RL) :-
forall(member(M, L), is_list(M)), !,
maplist(rev_lists, L, L0),
reverse(L0, RL).
rev_lists(L, L).
This one works by testing if all elements of a list L are themselves lists (M); if so, it will recursively apply itself (via maplist) over all individual sub-lists, else it will return the same list. This has the required effect.
Secondly, here's rev_lists/2 again, but written such that it doesn't rely on built-ins except member/2 (which is common):
rev_lists(L, RL) :-
reversible_list(L), !,
rev_lists(L, [], RL).
rev_lists(L, L).
rev_lists([], Acc, Acc).
rev_lists([L|Ls], Acc, R) :-
( rev_lists(L, RL), !
; RL = L
),
rev_lists(Ls, [RL|Acc], R).
reversible_list(L) :-
is_a_list(L),
\+ (
member(M, L),
\+ is_a_list(M)
).
is_a_list([]).
is_a_list([_|_]).
It's basically the same strategy, but uses an accumulator to build up reverse lists at each level, iff they are comprised exclusively of lists; otherwise, the same list is returned.

Related

Prolog predicate to multiply two lists (representing unary numbers)

I want to write a code that multiplies lists representing a number, like:
?- times([1,1,1], [1,1], Res).
Res = [1,1,1,1,1,1].
times([], _, []). % base case
times([_|T], Lis, [Lis|H]) :-
times(T, Lis, H).
I already have the code above and it kinda does what I want but not really. For example when asking:
?- times([1,1,1], [1,1], Res)
Res = [[1,1],[1,1],[1,1]].
The idea is there, but I just don't know how to fix that, I understand why it's happening (I'm adding a list as head), so I just wondered if anybody could help me.
Thanks in advance.
[Lis|H] will use Lis as first element, regardless whether Lis is a list or not. You should take a look at append/3 [swi-doc] for example to append two lists:
times([], _, []).
times([_|T], Lis, R) :-
append(Lis, H, R),
times(T, Lis, H).

Get duplicate items in a list and display answer in a list

I'm trying to find duplicate (non unique) items in a list.
For example from
duplicate([a,b,c,a,b,r,d,c], R).
I need to get [a,b,c].
I've written so far prolog program that finds duplicate elements in a list.
However, I get the answer as single elements.
R = a
R = b
R = c
And I have to get them in a list as [a,b,c]
duplicate([First|Rest], Element) :-
duplicate_first(Rest, First, Element).
duplicate_first([Head|Rest], X, X) :-
duplicate_second(Rest, Head, X).
duplicate_first([Head|Rest], _, X) :-
duplicate_first(Rest, Head, X).
duplicate_second(_, X, X).
duplicate_second([Head|Rest], _, X) :-
duplicate_second(Rest, Head, X).
P.S. I don't want to use any swi-prolog build in functions.
I find it a bit of a straightjacket to ignore the standard library. But you can fulfill the requirement by implementing the two predicates you need, which are member/2 and delete/3, yourself:
my_member(X, [X|_]).
my_member(X, [_|Xs]) :- my_member(X, Xs).
This is probably the most straightforward way to implement member/2, although it isn't exactly the same as in the SWI library.
my_delete([], _, []).
my_delete([X|Xs], X, Ys) :- my_delete(Xs, X, Ys).
my_delete([Y|Xs], X, [Y|Ys]) :- X \= Y, my_delete(Xs, X, Ys).
I just sort of took a crack at this and it seems to be OK. A better implementation would probably use something like X = Y -> ... but that can lead to issues with backtracking so I am using this variation which probably has other problems.
Now that you have the preliminaries, the actual implementation is not that hard. First your base case. There are no duplicates in the empty list.
duplicates([], []).
Now you have two inductive cases. One, in which the head of the list occurs inside the tail of the list. When that happens you add it to the result and remove it from the tail (or you'll get duplicates in your list of duplicates).
duplicates([X|Xs], [X|Ys]) :-
my_member(X, Xs),
my_delete(Xs, X, XsWithoutX),
duplicates(XsWithoutX, Ys).
Your other case is when the head element does not appear in the tail, so you can simply recur and find the rest of the duplicates:
duplicates([X|Xs], Ys) :-
\+ my_member(X, Xs),
duplicates(Xs, Ys).
I'm a little out of practice so the following code can be simplified but... given a filter function (that remove the Val correspondences from a list and return [Val] if correspondence is found or empty list otherwise)
filter(_, [], [], []).
filter(Val, [Val | Rest], [Val], LR) :-
filter(Val, Rest, _, LR).
filter(Val1, [Val2 | Rest], LO, [Val2 | LR]) :-
Val1 \= Val2,
filter(Val1, Rest, LO, LR).
and given a sort of optional adder in front of a list
addOptional([], L, L).
addOptional([O], L, [O | L]).
I suppose you can write duplicate/2 as follows
duplicate([], []).
duplicate([First | Rest], Result) :-
filter(First, Rest, Opt, Filtered),
duplicate(Filtered, Res2),
addOptional(Opt, Res2, Result).

Remove duplicate from a list but not returning two same results in SWI-Prolog?

duplicate([],[]).
duplicate([A|B],[A|B1]) :- not(member(A,B)), duplicate(B,B1).
duplicate([A|B],List) :- member(A,B), duplicate(B,List).
I wrote this predicate to remove duplicate from the list, but when I test it,
?- duplicate([a,b,c,a,d,c,b,a,e,f],N).
N = [d, c, b, a, e, f] ;
N = [d, c, b, a, e, f] ;
false.
Is there a way to just keep one result only, not two same results? (so it will only return one list).
Also, I am not allowed to use operators that modify the backtracking search, such as the cut operator !, the negation operators not, +, or the if-then-else operator with -> and ;
It would be grateful if someone could help me . :D
The actual reason for receiving more than one answer is the goal member(A,As). It produces multiple answers for duplicates in As.
?- member(a, [a,a]).
true
; true.
There are several ways out.
memberchk/2 or once/1
memberchk/2 is defined as
memberchk(X, Xs) :-
once(member(X, Xs)).
This removes alternate answers. But then, it may remove otherwise valid solutions too. Consider:
?- memberchk(X, [a,b]), b = X.
false.
?- b = X, memberchk(X, [a,b]), b = X.
b = X.
So memberchk/2 is sensitive to the precise instantiation, which makes it a very brittle, impure predicate.
But it has one good point: It sticks to just one answer for
?- memberchk(a, [a,a]).
true.
So what would be ideal is a definition that is both pure and sticking to the first element. Enter
memberd/2
memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
dif(X, Y),
memberd(X, Ys).
In this definition, the recursive rule is only of relevance if the list element is different. Thus this rule will never apply to memberd(a, [a,a,a]).
Another problem in your definition is not(member(A,B)) which only behaves as intended, if A and B are sufficiently instantiated. Your definition fails for:
duplicate([a,X],[a,b]). although there is a solution: X = b.
Rather replace it by non_member/2.
Alternatively, in case you are interested in the most efficient solution, consider library(reif) available
for
SICStus and
SWI which leads to:
list_nub([], []).
list_nub([X|Xs], Ys0) :-
if_(memberd_t(X, Xs), Ys0 = Ys1, Ys0 = [X|Ys1]),
list_nub(Xs, Ys1).
Here's one way to remove all duplicates, not the most efficient but I think it's quite easy to understand the intention.
rm_duplicates(In, Out) :-
exclude(has_duplicate(In), In, Out).
has_duplicate(List, Case) :-
dif(I, J),
nth0(I, List, Case),
nth0(J, List, Case).
If you mean to make a list into a set:
list_to_set(List, Set).
It's documented: list_to_set/2

Reverse list of lists

I have a problem with my prolog code. I need to reverse all atomic elements of list.
Example: [1,2,[3,4]] -> [[4,3],2,1]
My solution:
myReverse([], []).
myReverse([H|T], X) :- myReverse(T, RT), myAppend(RT, H, X).
But it only gives me: [[3,4],2,1]
I think, I need to use is_list function and recursive call list if it's not atomic... but I am stuck... do you guys know how to write it?
Nearly. Consider this solution:
myReverse([], []) :- !.
myReverse([H|T], X) :-
!,
myReverse(H, NewH),
myReverse(T, NewT),
append(NewT, [NewH], X).
myReverse(X, X).
The first clause is the base case, which includes a cut (!) to exclude choices left because of the last clause.
The second clause reverses the head H, which may be an atom or a list. If H is an atom, the recursive subgoal after the cut evaluates with the last clause, and atoms are passed through unchanged. If H is a list, it is evaluated with the second clause and all elements are reversed. The next subgoal does the same with the remainder of the list (the tail, T), then are finally concatenated using the built-in append/3. Note that the new head element NewH is singular, so needs to be added to a singleton list structure as [NewH] as per the definition of append/3 which operates on lists.
The last clause passes all other things (i.e., atoms, numbers, etc. - anything that isn't a list or a variable) through unchanged.
revall(L, Y) :-
revall(L, [], Y).
revall([], Y, Y).
revall([H|T], T2, Y) :-
is_list(H),!,
revall(H, Hr),
revall(T, [Hr|T2], Y).
revall([H|T], T2, Y) :-
revall(T, [H|T2], Y).
here without append

flatten list by replacing comma with plus operator inside a list in prolog

I am working on a scenario in Prolog (eclipse) wherein I need a list structure to be reformatted.
I have a list of the form:
MyList = [a,b,c].
I was trying to see if I can flatten the list to a single element with all the commas replaced with the + operator.
So my result list would look like:
ResultList = [a+b+c]
which is a single element list. The length of the initial list is arbitrary.
I know prolog is not suited for such operations, but can this be done?
here it is, in standard Prolog. I think there should be no difference with Eclipse:
list_to_op([X,Y|T], [R]) :-
list_to_op(T, X+Y, R).
edit: bug noted by false
list_to_op([X], [X]).
list_to_op([X], R, R+X).
list_to_op([X|T], R, Q) :-
list_to_op(T, R+X, Q).
test:
?- list_to_op([a,b,c],X).
X = [a+b+c] .
The accumulator is required to give the appropriate associativity: the simpler and more intuitive definition
list_to_op1([X], X).
list_to_op1([X|R], X+T) :-
list_to_op1(R, T).
gives
?- list_to_op1([a,b,c],X).
X = a+ (b+c) .
If evaluation order is important, use list_to_op.
edit:
there is a bug: list_to_op([a,b],X) fails.
here the correction, as often happens, it's a simplification:
list_to_op([], R, R).
list_to_op([X|T], R, Q) :-
list_to_op(T, R+X, Q).
This may help
flatten_list(A,[B]) :- flatten_list_inner(A,B).
flatten_list_inner([A],A).
flatten_list_inner([H|T],H+Y) :- flatten_list_inner(T,Y).
The output is slightly different from what you wanted. It is currently [a + (b + c)]
How about this non-recursive version..
list_to_op(L, Res) :-
concat_atom(L, '+', Atom),
Res = [Atom].
?- list_to_op([a,b,c], X).
X = ['a+b+c'].
Edit: This works in Swi-prolog.. not sure about Eclipse.