This is the pretty known code for an inorder traversal in a generic binary tree in PROLOG:
inorder(t(K,L,R), List) :-
inorder(L,LL), inorder(R, LR),
append(LL, [K|LR], List).
inorder(nil, []).
if the input binary tree is a binary search tree (BST), what about If I want to modify such code in such way I will not visit just all the nodes, but only the minimum needed to find those ones whose key falls in a given range, having them as output result?
I am trying something like:
inorder(R1, R2, t(K,L,R), List) :-
(K >= R1 -> inorder(R1, R2, L, LL); true),
(K =< R2 -> inorder(R1, R2, R, LR); true),
append(LL, [K|LR], List).
inorder(_, _, _, _).
I figured out the correct version:
inorder(R1,R2, t(K,L,R), List) :-
( R2 < K
-> inorder(R1,R2, L, LL),
append(LL,[],List);
K < R1
-> inorder(R1,R2, R, LR),
append(LR,[],List);
inorder(R1,R2, L, LL),
inorder(R1,R2, R, LR),
append(LL, [K|LR], List)
).
you are 'near to target', and you will benefit of studying what your code does. Here is my solution (only minimally tested)
inorder(R1,R2, t(K,L,R), List) :-
( R2 < K
-> inorder(R1,R2, L, List)
; K < R1
-> inorder(R1,R2, R, List)
; inorder(R1,R2, L, LL),
inorder(R1,R2, R, LR),
append(LL, [K|LR], List)
).
inorder(_R1,_R2, -, []).
of course, I like best a DCG solution
inorder(R1,R2, Tree, Yield) :-
phrase(inorder(R1, R2, Tree), Yield, []).
inorder(R1,R2, t(K,L,R)) -->
{R2 < K}
-> inorder(R1,R2,L)
; {K < R1}
-> inorder(R1,R2,R)
; inorder(R1,R2,L),
[K],
inorder(R1,R2,R).
inorder(_R1,_R2, -) --> [].
note: no more append/3 needed. It's cleaner and more efficient.
edit better naming:
inorder(R1,R2, Tree, Yield) :-
phrase(rangequery(R1, R2, Tree), Yield, []).
rangequery(R1,R2, t(K,L,R)) -->
{R2 < K}
-> rangequery(R1,R2,L)
; {K < R1}
-> rangequery(R1,R2,R)
; rangequery(R1,R2,L),
[K],
rangequery(R1,R2,R).
rangequery(_R1,_R2, -) --> [].
and sample run
?- inorder(1,2,t(3,t(1,-,-),-),L).
L = [1] ;
false.
?- inorder(1,4,t(3,t(1,-,t(2,-,-)),-),L).
L = [1, 2, 3] ;
false.
Related
Let's say you have a binary search tree:
t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil)))
which is:
73
/ \
31 101
/ / \
5 83 200
/
97
I need to write a predicate subtree(X1,X2,T) that would take 2 values from the tree (X1 and X2) and find the smallest common parent for them, and store its subtree in T.
So for the example above, if I query : subtree(83,200,X).
I should be getting back:
t(101,t(83,nil,t(97,nil,nil)),t(200,nil,nil))
which is:
101
/ \
83 200
/
97
Since 101 is the smallest common value to both of my numbers, I get that subtree back. How could I do that?
Thanks!
Here is my code for this problem. Just call
tree(X),common_ancestor(83,200,X,Y)
You will get your answer in Y.
tree3(X) :- X = t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil))).
% Chech membership in tree:
in(X, t(X, _, _)).
in(X, t(_, L, _)):-
in(X, L).
in(X, t(_, _, R)):-
in(X, R).
% Getting subtree given the value of root
get_subtree(X, t(X, L, R),Res) :- Res = t(X,L,R).
get_subtree(X, t(_, L, _),Res):-
get_subtree(X, L,Res).
get_subtree(X, t(_, _, R),Res):-
get_subtree(X, R,Res).
% least_common_ancestor(N1, N2, Tree, Res) assignes the value of the least common ancestor to Res.
% Note that it's assumed all nodes have different values.
% Base cases: when one value is the parent of the other, then the parent is the LCA:
least_common_ancestor(N1, N2, t(N1, L, R), N1):-
(in(N2, L) ; in(N2, R)), !.
least_common_ancestor(N1, N2, t(N2, L, R), N2):-
(in(N1, L) ; in(N1, R)), !.
% If one is in the left (right) subtree and the other is in the right (left) subtree then the current root is the LCA
least_common_ancestor(N1, N2, t(X, L, R), Res):-
((in(N1, L), in(N2, R)) ; (in(N1, R), in(N2, L))), !,
Res = X.
% Otherwise, recurse to both subtrees:
least_common_ancestor(N1, N2, t(_, L, _), Res):-
least_common_ancestor(N1, N2, L, Res), !.
least_common_ancestor(N1, N2, t(_, _, R), Res):-
least_common_ancestor(N1, N2, R, Res).
% The main function
commonGP(Ka,Kb,T,ST) :-
least_common_ancestor(Ka,Kb,T,Res), get_subtree(Res,T,ST).
I wrote a predicate that is supposed to traverse through a list of numbers and compare current number to the next one then adds the bigger number to a list that it is supposed to return. The last number is simply added to the list.
For example:
[1,2,3] should return [2,3,3]
[3,5,6,6,5,9] should return [5,6,6,6,9,9]
Problem
The predicate finds the answer (it writes it out), but it doesn't unify(?) with it and goes on to return [].
Code:
head([H|_], H).
head([],[]).
maximize([], X) :- write(X).
maximize([H|T], X) :-
(head(T, N), N = []) -> (append(X, [H], L), maximize([], L)) ;
(head(T, N), H < N) -> (append(X, [N], L), maximize(T, L)) ; (append(X, [H], L), maximize(T, L)).
A solution for the problem you describe is:
maximize([], []).
maximize([X| Xs], M) :-
maximize(Xs, X, M).
maximize([], X, [X]).
maximize([Y| Ys], X, M) :-
( Y > X ->
M = [Y| T]
; M = [X| T]
),
maximize(Ys, Y, T).
Sample calls:
| ?- maximize([1,2,3], M).
M = [2,3,3]
yes
| ?- maximize([3,5,6,6,5,9], M).
M = [5,6,6,6,9,9]
yes
This solution takes advantage of first-argument indexing to avoid spurious choice points.
This is the program I have:
foo(L,[H|R1]) :-
foo(L,R1,H).
foo([H],[],H).
foo([H|T],[H|T1],R) :-
foo(T,T1,R).
This is the query:
foo([1,2,3,4,5,6],X).
I don't understand what this program does, can someone help me and explain how does it work?
You could try to refactor it. If I start with:
foo(L, [H|R1]) :-
foo(L, R1, H).
foo([H], [], H).
foo([H|T], [H|T1], R) :-
foo(T, T1, R).
I can change the argument order foo(1,2,3) to foo(2,3,1):
foo(L,[H|R1]) :-
foo(R1, H, L).
foo([], H, [H]).
foo([H|T1], R, [H|T]) :-
foo(T1, R, T).
Now I can change the 2-nd argument of foo, and pass [H] instead of H:
foo(L, [H|R1]) :-
foo(R1, [H], L).
foo([], H, H).
foo([H|T1], R, [H|T]) :-
foo(T1, R, T).
Now you can rename the predicates to roll and append:
roll(L, [H|R1]) :-
append(R1, [H], L).
append([], H, H).
append([H|T1], R, [H|T]) :-
append(T1, R, T).
In Prolog, there is no need to understand the source code. Instead, let Prolog do this for you. Just ask the most general query:
?- foo(L,R).
L = [_A],
R = [_A]
; L = [_A,_B],
R = [_B,_A]
; L = [_A,_B,_C],
R = [_C,_A,_B]
; L = [_A,_B,_C,_D],
R = [_D,_A,_B,_C]
; L = [_A,_B,_C,_D,_E],
R = [_E,_A,_B,_C,_D]
; L = [_A,_B,_C,_D,_E,_F],
R = [_F,_A,_B,_C,_D,_E]
; L = [_A,_B,_C,_D,_E,_F,_G],
R = [_G,_A,_B,_C,_D,_E,_F]
; L = [_A,_B,_C,_D,_E,_F,_G,_H],
R = [_H,_A,_B,_C,_D,_E,_F,_G]
; ... .
Do you see a pattern here?
To understand it easier, put the recursive clause above the base one:
foo( [H | T], [H | T1], R) :- foo( /* T = [_|_] */
T, T1, R).
foo( [R], [], R).
So we advance along the two lists (that hold the same elements, H) until we hit the last element in the first list ([R]), at which point the second list is exhausted ([]), and we get hold of that last element (R).
This means that foo( A, B, R) :- append( B, [R], A).. Thus,
foo( L, [E | R1]) :- % foo( L, R1, E).
append( R1, [E], L). % R1 ++ [E] = L
i.e. foo( L, M) relates two lists L, M, where L is M with its first element E "flipped" to the list's end:
L : .............E
M : E.............
% ---- R1 -----
I have an error when i count number of check in order to build the list.
Here is my code:
find(_R1, _R2, [], [],0).
find(R1, R2, [[Mark, CName] | T], L,N) :-
( (R1 =< Mark, Mark =< R2),N is N+1
-> L = [CName | L1]
; L = L1
),
find(R1, R2, T, L1,N).
I get the following error:
ERROR: is/2: Arguments are not sufficiently instantiated.
N is N+1 is always false in Prolog , Prolog is not C, you MUST write N1 is N+1, because when N is unified with a value, it can't be changed, so N is never equal to N+1.
Your code must be written like that, the recursion must be called before the calculus of N.
find(_R1, _R2, [], [],0).
find(R1, R2, [[Mark, CName] | T], L,N):-
find(R1, R2, T, L1,N1),
( (R1 =< Mark, Mark =< R2)
-> L = [CName | L1],N is N1+1
; L = L1, N = N1).
[EDIT] In your post, N has no value when you call N is N+1, because N gets a value after the return of the call of find(R1, R2, T, L1,N).
i need to covert list to binary search tree ,then search about range of ages in this tree and return the a list contain these values, and also return number of checks in order to build the output list .
i spend two days trying do this but it always return false
here my last code that i reach it with help of mbratch:
my_list( [[30,'john'], [58,'alex'], [14,'randy'], [65,'shawn'], [67,'jack']] ).
construct(L,T) :- construct(L,T,nil).
construct([],T,T).
construct([N|Ns],T,T0) :- add(N,T0,T1), construct(Ns,T,T1).
add(X, nil, node(X, nil, nil)).
add(X, node(Root, L, R),node(Root, L1, R)) :- X #< Root, add(X, L, L1).
add(X, node(Root, L, R),node(Root, L, R1)) :- X #> Root, add(X, R, R1).
findInRange(R1, R2, T, S, N) :- find(R1, R2, T, S, N),!.
find(_R1,_R2, nil, [], 0).
find(R1, R2, node([Age,Name],L,R), S, N) :-
R1 =< Age,R2 >= Age, % is the age OK (in range), if it is check left and
find(R1, R2, L, LL, NL),
find(R1,R2,R,LR,NR),
append([[Age,Name]],LL,X),
append(X,LR,S),
N is NL+NR+1.
find(R1, R2, node([Age,Name],L,R), [], 0) :-
Age > R2;Age<R1. % if the age is greater than R2, return []
find(R1, R2, node([Age,Name],L,R), LL, N) :-
R1 < Age, % if the age is bigger than R1 search the left tree return LL
find(R1,R2,L,LL,NL),
N is NL+1.
find(R1, R2, node([Age,Name],L,R), LR, N) :-
R2 > Age, % if the age smaller than R1 search the right tree return LR
find(R1,R2,R,LR,NR),
N is NR+1.
and here is my query :
my_list(Z), construct(Z, T), findInRange(11, 15, T, S, N).
it should retufn [[14,'randy']] and number of checks.
Why does it return empty list and N=0 ?
I think this will do the trick. I ran the query you posted and got S = [[30, john], [14, randy]].
my_list( [[30,'john'], [58,'alex'], [14,'randy'], [65,'shawn'], [67,'jack']] ).
construct(L,T) :- construct(L,T,nil).
construct([],T,T).
construct([N|Ns],T,T0) :- add(N,T0,T1),construct(Ns,T,T1).
add(X, nil, node(X, nil, nil)).
add(X, node(Root, L, R),node(Root, L1, R)) :- X #< Root, add(X, L, L1).
add(X, node(Root, L, R),node(Root, L, R1)) :- X #> Root, add(X, R, R1).
findInRange(R1, R2, T, S, N) :- find(R1, R2, T, S, N),!.
find(_R1,_R2, nil, [], 0).
find(R1, R2, node([Age,Name],L,R), S, N) :-
R1 =< Age,R2 >= Age, % is the age OK (in range), if it is check left and right side
find(R1, R2, L, LL, NL),
find(R1,R2,R,LR,NR),
append([[Age,Name]| LL],LR,S),
N is NL+NR+1.
find(R1, R2, node([Age,Name],L,R), LL, N) :-
Age > R2, % if the age is bigger than R2 search the left tree return LL
find(R1,R2,L,LL,NL),
N is NL+1.
find(R1, R2, node([Age,Name],L,R), LR, N) :-
R1 > Age, % if the age smaller than R1 search the right tree return LR
find(R1,R2,R,LR,NR),
N is NR+1.