DLV list composition - list

I was wondering if there is a way in DLV for creating a list with the elements of all predicates that are true in a rule. For example, if I have the following predicates
foo(a, b).
foo(a, c).
foo(a, e).
foo(b, c).
The result I am looking for should be new predicates where the first element is the first parameter of foo and the second parameter should contain a list with all the elements associated to the first parameter. Empirically:
bar(a, [b,c,e]).
bar(b, [c]).
I know there is a way of getting these results (plus many more) with the following code:
bar(A, [X]) :- foo(A, X).
bar(A, P ) :- bar(A, P0),
foo(A, X),
not #member(X, P0),
#insLast(P0, X, P).
But I would like to know if there is a way of preventing the generation of all possible lists of size from 1 to N (being N the number of elements of the final list). I would like to do it for two reasons: (1) reduce computational cost (2) prevent discarding all unnecessary predicates.
If the computational cost was not a problem, which may be the case, I was thinking of the following changes in order to keep only the predicates with the largest lists:
tmp_bar(A, [X], 1) :- foo(A, X).
tmp_bar(A, P, L) :- tmp_bar(A, P0, L0),
foo(A, X),
not #member(X, P0),
#insLast(P0, X, P),
L = L0 + 1.
bar(A, P) :- tmp_bar(A, P, L),
max_list(A, L).
max_list(A, L) :- foo(A, _),
#max{X: tmp_bar(A, P, X)} = L.
However, this starts to get complicated and is showing all of the lists of maximum size and not only one of them. How do I get rid of all but one? I tried generating bar(A,P) only in case their is no other bar(A, _) but I get "rule is not safe". Also tried counting the number of occurrences and similar problems appear...
Most importantly, is it possible to get the results I expect all at once without that many tricks?
Any help is appreciated,
Thanks!

Apparently I found a solution to the problem by adding elements in a particular order. What I do is to add the element at the end of the list only if its smaller than the last element of the current list. I was dealing with names rather than numbers so I though this wasn't possible).
Here is the code:
tmp_bar(A, [X], 1) :- foo(A, X).
tmp_bar(A, P, L) :- tmp_bar(A, P0, L0),
foo(A, X),
#last(P0, Y),
Y < X,
#insLast(P0, X, P),
L = L0 + 1.
bar(A, P) :- tmp_bar(A, P, L),
max_list(A, L).
max_list(A, L) :- foo(A, _),
#max{X: tmp_bar(A, P, X)} = L.
Hope it helps someone else in the future.

Related

Separate list by first occurence of variable in SWI-Prolog without instantiating the variable itself

I have following source-code:
split_list(List, L, [R1|RN], Condition) :-
find_first(List, R1, Condition),
append(L, [R1|RN], List),
forall(member(X, L),
not(call(Condition, X))).
find_first([H|_], H, Condition) :- call(Condition, H), !.
find_first([_|T], X, Condition) :- find_first(T, X, Condition).
This Prolog program splits a list List into two lists L and [R1|RN]. R1 is the first element of List which satisfies the predicate Condition. L contains all elements in List before R1. L does not contain any element satisfying Condition. RN contains all elements which follow R1 in List.
My aim now is to write some predicate, which separates some list [a,b,c,D,d,f,e,f,d] into two lists [a, b, c] and [D, d, f, e, f, d] without instantiating the variable D.
I just tried the call:
split_list([a,b,c,_,d,f,e,f,d], L, R, var).
but this produces much solutions by instantiating _ by a or b or c and so on. How can I solve it?
From the looks of it, this is a more useful find_first (with argument order changed to be more sensible):
% P means previous, R means remainder
find_first_succ(Cond, L, P, [H|T]) :-
find_first_succ_(L, Cond, P, [H|T]).
find_first_succ_([H|T], Cond, [], [H|T]) :-
call(Cond, H), !.
find_first_succ_([H|T], Cond, [H|P], R) :-
find_first_succ_(T, Cond, P, R).
Result in swi-prolog:
?- find_first_succ(var, [a,b,c,_,d,f,e,f,d], P, R).
P = [a, b, c],
R = [_, d, f, e, f, d].
So, you don't need that problematic append.
I found some solution:
split_list(List, L, [R1|RN], Condition) :- member(R1, List), append(L, [R1|RN], List), call(Condition, R1).

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

Gathering results of methods in one List with Prolog

So I am trying to
I am defining the sets with is_a(b, a), is_a(c, a), which for simplicity would look visually something like this:
a
b c
d e f g
I want to give in the list [b, c] and as a result get the list [d, e, f, g]
At the moment when I give in a node or a variable, then it can find everything that is underneath it with this method:
find_nodes(Root, Root) :-
\+ is_a(_, Root).
find_nodes(Root, X) :-
is_a(Node, Root),
find_nodes(Node, X).
Which when run gives me the result I need :
?- find_nodes(b, X).
X = d.
X = e.
But it is not in a list, so I have tried :
?- all_nodes([b, c], X).
all_nodes([], _).
all_nodes([H|T], [R|Res]):-
findall(L, find_nodes(H, L), R),
all_nodes(T, Res).
Which gives me - X = [[d, e], [f, g]|_4040], which consists of lists within lists, but I need just 1 list, that would be X = [d, e, f, g].
What am I doing wrong here?
EDIT
Like #lurker said findall returns a list and adding list to a list will give the result I get right now.
The one thing I also tried was using:
all_nodes([], _).
all_nodes([H|T], [R|Res]):-
find_nodes(H, R),
all_nodes(T, Res).
But well that one does not work either because It only gives me 1 element, which in this case is d and then f.
You can take advantage of the de facto standard findall/4 (*) predicate to solve the problem. This predicate is a variant of the standard findall/3 predicate that allows passing a tail for the list of solutions collected by the predicate. For example:
?- findall(N, (N=1; N=2; N=3), L, [4,5]).
L = [1, 2, 3, 4, 5].
In the following solution, I have renamed predicates and variables for clarity and modified your node leaf predicate:
is_a(a, b).
is_a(a, c).
is_a(b, d).
is_a(b, e).
is_a(c, f).
is_a(c, g).
leaf(Leaf, Leaf) :-
\+ is_a(Leaf, _).
leaf(Node, Leaf) :-
is_a(Node, Child),
leaf(Child, Leaf).
all_nodes([], []).
all_nodes([Node| Nodes], Leaves):-
findall(Leaf, leaf(Node, Leaf), Leaves, Tail),
all_nodes(Nodes, Tail).
Sample calls:
?- all_nodes([b, c], X).
X = [d, e, f, g].
?- all_nodes([a], X).
X = [d, e, f, g].
?- all_nodes([b], X).
X = [d, e].
(*) It's a built-in predicate in GNU Prolog, JIProlog, Lean Prolog, O-Prolog, SICStus Prolog, SWI-Prolog, XSB, and YAP (possibly others).

PROLOG Sum of a list created from facts

I want to create a list from the facts. And the list should contains only one of the arity in the facts.
For example :
%facts
abc(a, b, c).
abc(d, e, f).
abc(g, h, i).
Sample :
?-lists(A).
A = [a, d, g];
No.
EDIT :
Using the suggestion by Vaughn Cato in the comment, the code become this :
%facts
abc(a, b, c).
abc(d, e, f).
abc(g, h, i).
lists(A) :-
findall(findall(X, abc(X, _, _), A).
The list is created, but how to sum up the list A ?
If sum of list for input from query,
sumlist([], 0).
sumlist([X|Y], Sum) :-
sumlist(Y, Sum1),
Sum is X + Sum1.
But if want to sum the existing list, how to define the predicate?
To sum a list of numbers such as that produced by your definition of lists/1, most Prolog systems (e.g., GNU, SWI) implement sum_list/2 which takes a list of numbers as the first argument and binds their sum in the second:
?- sum_list([1,2,3],Sum).
Sum = 6.
You can also solve it with aggregate_all/3. It eliminates need to build list in memory if you just need a sum.
sum_facts(Template, Arg, Sum) :-
aggregate_all(sum(X), (call(Template), arg(Arg, Template, X)), Sum).
In this example I use a generic call with defined Template:
sum_facts(abc(_, _, _), 1, Sum).
If you will always use it with the first arg of abc/3 this version will be enough:
sum_facts(Template, Arg, Sum) :-
aggregate_all(sum(X), abc(X, _, _), Sum).
As suggested by Vaughn Cato, it's help me a lot by using findall(X,abc(X, _ , _ ),A). to create the list I wanted to.

Custom reverse of a list in Prolog

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.