I've been trying to solve this problem of mine for a while now but I'm not really sure how to go about it.
For example, let's say I have this "tree" in my database:
tree4(b(b(l(Apple),l(Banana)), b(l(Orange), l(Pear)))).
I want to be able to query the database so as to retrieve the information within each l() and present it in a list. So far I've done this:
leaves(l(X), L) :-
L = X.
leaves(b(X,Y), L) :-
leaves(X, A),
leaves(Y, B),
L = [A, B].
I then query the database and it gives me this:
?- tree4(T), leaves(T, L).
T = b(b(l(1), l(2)), b(l(3), l(4))),
L = [[1, 2], [3, 4]].
The problem with this code is it generates multiple lists nestled within my original one. Is there another way to go about this? Any help would be greatly appreciated!
As you are describing a list (in this case: of leaves), consider using a DCG:
leaves(l(L)) --> [L].
leaves(b(B1,B2)) --> leaves(B1), leaves(B2).
Example query (using atoms instead of variables in tree4/1):
?- tree4(Tree), phrase(leaves(Tree), Leaves).
Tree = b(b(l(apple), l(banana)), b(l(orange), l(pear))),
Leaves = [apple, banana, orange, pear].
You can avoid the cost of the append/3 predicate by using an accumulator to collect the leaves during the traversal of the tree:
leaves(Tree, Leaves) :-
leaves(Tree, [], Leaves).
leaves(l(Leaf), Leaves, [Leaf| Leaves]).
leaves(b(Left,Right), Leaves0, Leaves) :-
leaves(Right, Leaves0, Leaves1),
leaves(Left, Leaves1, Leaves).
Using your sample call:
?- leaves(b(b(l(1), l(2)), b(l(3), l(4))), Leaves).
Leaves = [1, 2, 3, 4].
Assuming your Prolog implementation has an append predicate, you could do this:
leaves(l(X), [X]).
leaves(b(X,Y), L) :-
leaves(X, A),
leaves(Y, B),
append(A, B, L).
So leaves will always return a flat list, even if there's just one. This also assumes your tree is strictly binary, as you have it described.
Just a reminder about flatten/2, an handy builtin:
?- leaves(b(b(l(1), l(2)), b(l(3), l(4))), L), flatten(L, F).
L = [[1, 2], [3, 4]],
F = [1, 2, 3, 4].
As you can see from documentation, its use is discouraged, and you have already received plenty of good hints that allow to avoid it.
Related
I'm having trouble figuring out how to find the sum of the integers that are in a list of pairs like so:
[[a, 1], [b, 2], [c, 3], [d, 4]]
I tried something like this, since it is reminiscent of a regular sum function:
sum([], 0).
sum([[_,Head]|[_,Tail]], Sum) :-
sum([_,Tail], Sum2),
Sum is Head+Sum2.
With the call being:
sum([[a, 1], [b, 2], [c, 3], [d, 4]], Total),
write('Sum = '), write(Total).
But that doesn't work. It prints out false, when it should print out the sum, which would be 10 here.
In your attempt to define the predicate sum/2, you're not handling the lists of lists correctly. Try:
sum(Lists, Sum) :-
sum(Lists, 0, Sum).
sum([], Sum, Sum).
sum([[_,N]| Lists], Sum0, Sum) :-
Sum1 is Sum0 + N,
sum(Lists, Sum1, Sum).
This version uses an accumulator to enable a tail-recursive definition. Sample call:
| ?- sum([[a, 1], [b, 2], [c, 3], [d, 4]], Sum).
Sum = 10
yes
I think it might help to split this into two tasks:
create a new list of the second item of each sublist; and
sum up that list.
This makes it easier to tackle the two problems, and furthermore you now have two extra predicates that can be used for other purposes.
We can obtain a list of the second item of the sublists with:
item2list([], []).
item2list([[_, X|_]|T], [X|T2]) :-
item2list(T, T2).
or we can use maplist/3 [swi-doc] and nth1/3 [swi-doc]:
item2list(L1, L2) :-
maplist(nth1(2), L1, L2).
or we can write item2list in terms of findall/3 [swi-doc] and member/2 [swi-doc]:
item2list(L1, L2) :-
findall(X, member([_,X|_], L1), L2).
although here the predicate is not bidirectional.
For example:
?- item2list([[a, 1], [b, 2], [c, 3], [d, 4]], L).
L = [1, 2, 3, 4].
I leave summing up that list as an exercise.
Whenever a goal fails that you expect to succeed, see this as an opportunity to learn (short form for logic earn = earn logic). After all, this is Prolog which was meant to mean Programming in Logic. So where is the logic in your program?
For the moment your program fails, but you expected it to succeed. Where is the culprit? Let's generalize your program such that the resulting program still fails, but is much smaller. There are two easy ways to generalize a program:
remove goals (by adding a prefix *)
remove terms (replacing term by _/*term*/
We can do this pretty blindly. No need to understand your program. Just recheck that the goal still fails. Here is what I came up with on my first try:
:- op(950, fy, *).
* _G_0. % ignore the argument _G_0
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail]], Sum) :-
* sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- sum([_/*[a, 1]*/, _/*[b, 2]*/, _/*[c, 3]*/, _/*[d, 4]*/], Total).
false. % gnah - still fails
One problem has to be in the remaining visible part. Too difficult to figure out? Let Prolog explain it to you by querying the most general query:
?- sum(Xs, Sum).
Xs = []
; Xs = [_A,_B,_C].
So only two lengths of lists are possible: The empty list and a list with three elements. Note that we have currently a generalized version of the predicate. So there is no guarantee that we will find solutions for both lengths. However, we can be 100% sure that for all other lengths there will be no solution.
Let's get back at the original program and ask the most general query:
?- sum(Os, Total).
Os = [], Total = 0
; false.
Oh no, there is a single solution only. And not even a single solution for sum([_|_], Total).
So let's generalize the program again but now with respect to this failing goal:
sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail|_/*[]*/]], Sum) :-
sum([_,Tail], Sum2),
* Sum is Head+Sum2.
?- Os = [_|_], sum(Os, Total).
false.
In this part there must be a further error. And in fact, the goal sum([_,Tail], Sum2) is the culprit: It is about a list of exactly two elements, but the rule wants at least three
For the actual fixes, see the other answers.
This method works for pure, monotonic programs such as yours.
So I am having a list of lists [[],[1],[2,3]] and I want to merge this into a single list.
This is was I tried so far I GNU Prolog:
function([],[]).
function([Varlist|Vars],Var):-
function(Varlist,Var1),
function(Vars,Var2),
append(Var1,Var2,Var).
but this fails when I get the number 1 out, been trying for hours now.
Hope you can help : )
If you consider this generalization:
function1(L, L).
function1([Varlist|Vars],Var) :-
function1(Varlist,Var1),
function1(Vars,Var2),
append(Var1,Var2,Var).
you will see that we get more than required:
?- function1([[],[1],[2,3]], R).
R = [[], [1], [2, 3]] ;
R = [[1], [2, 3]] ;
R = [1, [2, 3]] ;
R = [1, 2, 3] ;
false.
We should keep just the last solution. Not so easy... Revert to original code:
function2([], []).
function2([Varlist|Vars],Var) :-
...
If we assume Varlist is a list, then we will use immediately as argument to append/3 to get Var, and we just recurse to 'flat out' Vars (remaining list of lists)
function2([], []).
function2([Varlist|Vars], Var) :-
function2(Vars, VarTemp1),
append(Varlist, VarTemp1, Var).
Indeed so we solve the problem:
?- function2([[],[1],[2,3]], R).
R = [1, 2, 3].
but there is a bug, not easy to spot. It turns out that solving it also make the procedure more efficient. See if you can find the bug and solve it...
I have written a program that can take a list and change it into a tree.
build_tree([X,Y],'Tree'(X,Y)) :- !.
build_tree([X|Y],'Tree'(X,Z)) :- build_tree(Y, Z).
If I want to reverse the process and take the tree and change it back into a list, how would I do this?
Note that your tree->list conversion isn't a function since trees may correspond to multiple lists:
?- build_tree([1, 2, 3], T).
T = 'Tree'(1, 'Tree'(2, 3)).
?- build_tree([1, 'Tree'(2, 3)], T).
T = 'Tree'(1, 'Tree'(2, 3)).
If you want a predicate that can generate all lists from a tree, remove the cut from build_tree and apply it with a variable first argument. If you want a deterministic conversion, write a new predicate tree_to_list.
Just curious, how would that deterministic version play out? Assuming there was only one possible list, from the tree, for example:
('Tree'('Tree'(nil, 2, nil), 5, 'Tree'(nil, 6, nil)).
Which gives: L = [5, 2, 6]
If you remove the cut from the first rule, your code it's ready to work in 'backward' mode:
?- build_tree([1,2,3,4],T).
T = 'Tree'(1, 'Tree'(2, 'Tree'(3, 4))) ;
false.
?- build_tree(X,$T).
X = [1, 'Tree'(2, 'Tree'(3, 4))] ;
X = [1, 2, 'Tree'(3, 4)] ;
X = [1, 2, 3, 4] ;
false.
flatten(leaf, []).
flatten(node(L, E, R), Ls) :-
flatten(L, Ls1),
append(Ls1, [E], Ls2),
flatten(R, Ls3),
append(Ls2, Ls3, Ls).
if you consider tree ass node(leaf,Element,leaf) for example
flatten(node(node(leaf,2,leaf),3,node(leaf,5,leaf)),X).
gives X=[2,3,5].
and if you wanna have bst
List to Tree.
insert(E,leaf,node(leaf,E,leaf)).
insert(E,node(L,N,R),T) :-
E >= N,
T=node(L,N,R1),
insert(E,R,R1).
insert(E,node(L,N,R),T) :-
E < N,
T=node(L1,N,R),
insert(E,L,L1).
list_to_tree(List,Tree) :-
list_to_tree(List,leaf,Trea2),
Tree=Trea2.
list_to_tree([],Tree,Tree).
list_to_tree([H|T],Tree,St):-
insert(H,Tree,R),
list_to_tree(T,R,St).
How do you use the permute predicate to output into a list in SWI prolog?
The permutation/2 predicate only returns one result at a time.
The most straight forward way to describe all permutations is using bagof/3. Note that findall/3 cannot be used directly, since findall produces literal copies of the original list.
list_allperms(L, Ps) :-
bagof(P, permutation(L,P), Ps).
?- L = [A,B,C], list_allperms(L, Ps).
L = [A, B, C], Ps = [[A,B,C],[A,C,B],[B,A,C],[B,C,A],[C,A,B],[C,B,A]].
So that's the no-brainer version. But you can even implement it directly in pure Prolog without any auxiliary built-ins.
If you want a list of all permutations, findall/3 is the way to go. If you want to print, you can use forall/2. Either case:
case_a(In, L) :- findall(Perm, permutation(In, Perm), L).
case_b(In) :- forall(permutation(In, Perm), writeln(Perm)).
forall it's a general purpose builtin, implementing a failure driven loop, amazing for its' simplicity. I report the definition from SWI-Prolog library, I think it's interesting.
%% forall(+Condition, +Action)
%
% True if Action if true for all variable bindings for which Condition
% if true.
forall(Cond, Action) :-
\+ (Cond, \+ Action).
EDIT:
As noted by false, if you have variables in your list, you should be aware of the different behaviour of findall/3 WRT bagof/3:
?- case_a([1,2,3],X).
X = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]].
?- case_a([A,B,C],X).
X = [[_G467, _G470, _G473], [_G455, _G458, _G461], [_G443, _G446, _G449], [_G431, _G434, _G437], [_G419, _G422, _G425], [_G407, _G410, _G413]].
Note that each variable in in the second query output is different: that could be the request outcome, or not, depending on the problem at hand. Deciding the appropriate behaviour WRT the variable quantification is mandatory in the restricted class of problems where variables are data, i.e. metaprogramming, I think...
permutation([], []).
permutation(L, [X|Xs]) :- select(X, L, Rest), permutation(Rest, Xs).
If I type permutation([1,2,3],R), the first solution is "[1,2,3]" but how to get to the second one without using ";" or "fail". I need to use the 2nd solution like "[1,3,2]" or so in order compare it to another list.
What I mean is:
permutation([], []).
permutation(L, [X|Xs]) :- select(X, L, Rest), permutation(Rest, Xs).
go_perm(L,P) :-
L = P,
write(P),nl.
go_perm(L,P) :-
permutation(P,P2), % in this case i wanna get the next solution -.-
go_perm(L,P2).
If L = P then it finishes. Permutation of the first solution for "[1,2,3]" is "[1,2,3]". But that pulls me into stackoverflow because it runs into never-endless thing.
Perhaps you understand me. Thanks!
Assuming you want to loop over the solutions to print them
One standard way to accomplish this is to fail and backtrack, as in:
print_all_permutations(X)
:- permutation(X, Y), print(Y), nl, fail ; true.
Assuming you just want to check if a given solution is correct
You are already done. Just call the function with the reference list and the list you want to test:
permutation([1, 2, 3], [2, 1, 3]).
will return true, because [2, 1, 3] is a permutation of [1, 2, 3]. If the second argument is not a permutation, the goal will evaluate to false.
This is the magic of prolog: finding a solution, or checking if a given solution is correct, are the same thing.
In between: partial solution
The same reasoning still applies:
permutation([1, 2, 3], [2, X, 3]).
will display the only possible value for X.
Or, if you want the whole list to be the result:
X = [2, X, 3], permutation([1, 2, 3], X).
You need to look at various aggregate predicates. Here, findall would work nicely. you can invoke it:
ListIn=[1,2,3], findall(Perm, permutation(ListIn, Perm), Permutations).
This will call permutation on ListIn until it fails. Each Perm returned by permutation will be collected into the Permutations variable.
permutation is a predicate that succeeds when one list is a permutation of the other. You don't actually need to enumerate them; just write permutation([1, 2, 3], [2, 1, 3]) and Prolog will tell you "true".