How to make fact(any) in Prolog? - prolog

I have database which consists of list of trees and facts about those trees. For example:
softness(soft).
softness(hard).
softness(veryhard).
color(gray_brown).
color(soft_red).
color(light).
color(dark).
wood(oak, leafes(leafed), softness(hard), color(gray_brown), on_touch(smalltexture)).
And I'm trying to make rule which will ask user input on specific parameters of tree and then seek for appropriate one. Like this.
what_wood(A, B):-
wood(A, B, _, _, _);
wood(A, _, B, _, _);
wood(A, _, _, B, _);
wood(A, _, _, _, B);
wood(A, B, _, _); %I have one tree with three parameters =/
wood(A, _, B, _);
wood(A, _, _, B).
what_wood(A) :-
write('Leafes: '), read(X), what_wood(A, leafes(X)),
write('Softness: '), read(Y), what_wood(A, softness(Y)),
write('Color: '), read(Z), what_wood(A, color(Z)),
write('On touch: '), read(Q), what_wood(A, on_touch(Q)).
So my question - if user wants to specify parameter as "any" is there a way to do something like this?
leafes(leafed).
leafes(coniferous).
leafes(any):-
leafes(X). %this one doesn't work. Prints false
%leafes(leafed);leafes(coniferous). %Of course this doesn't work too.
(Sorry for my English :) )
=====UPDATE=====
I ended up with this code which works fine thanks to you :)
Will add check for user input also.
wood(oak, leafed).
wood(oak, hard).
wood(oak, gray_brown).
wood(oak, smalltexture).
wood(beech, leafed).
wood(beech, hard).
wood(beech, soft_red).
wood(beech, largetexture).
wood(yew, leafed).
wood(yew, veryhard).
wood(yew, dark).
...
what_wood(A, B, C, D, E):-
wood(A, B), wood(A, C), wood(A, D), wood(A, E).
what_wood(A) :-
write('Leafes: '), read(X), convert(X, Leaves),
write('Softness: '), read(Y), convert(Y, Softness),
write('Color: '), read(Z), convert(Z, Color),
write('On touch: '), read(Q), convert(Q, OnTouch),
what_wood(A, Leaves, Softness, Color, OnTouch).
convert(any, _) :-
!.
convert(Attrib, Attrib).
This code returns same answers like
A = oak ;
A = oak ;
...
A = beech ;
A = beech .
But this is other story which have nothing to do with current question.

Assuming that the number of wood attributes is fixed, four in your example, you can define a predicate wood/5with facts such as:
% wood(Wood, Leaves, Softness, Color, OnTouch).
wood(oak, leafed, hard, gray_brown, smalltexture).
Then, you can modify your what_wood/1 predicate such that when the user enters the atom any for an attribute, it uses an anonymous variable when trying to match wood/5 facts. Something like:
what_wood(Wood) :-
write('Leafes: '), read(Leafes0), convert(Leafes0, Leafes),
write('Softness: '), read(Softness0), convert(Softness0, Softness),
write('Color: '), read(Color), convert(Color0, Color),
write('On touch: '), read(OnTouch), convert(OnTouch0, OnTouch),
wood(Wood, Leaves, Softness, Color, OnTouch).
convert(any, _) :-
!.
convert(Attribute, Attribute).
The next step would be to check the validity of the values entered by the user and e.g. repeat the question if invalid. For example, you could define a read_attribute/2 predicate that would do the reading repeating it until the user enters a valid value:
read_attribute(Attribute, Value) :-
repeat,
write('Value for '), write(Attribute), write(': '),
read(Value),
valid_attribute(Attribute, Value),
!.
valid_attribute(leafes, leafed).
valid_attribute(leafes, coniferous).
valid_attribute(leafes, any).
...
This can be improved in several ways. E.g. by printing the possible values for an attribute when asking its value so that the user knows what is accepted as valid values. The predicate valid_attribute/2 can also be rewritten to avoid creating choice points when testing. You can also rewrite this predicate to take advantage of the facts you already have for valid attributed values:
valid_attribute(Attribute, Value) :-
Test =.. [Attribute, Value],
once(Test).

Prolog is a language with a clean relational data model. I would choose a different schema, separating each attribute: like
wood(oak, leafes(leafed)).
wood(oak, softness(hard)).
...
in this way you can rely on the usual relational patterns to apply in your 'application'. Specifically, Prolog use queries as procedures...

Related

Usage of distinct still leading to duplicates

I'm new to prolog, and as I understand it, the purpose of 'distinct' is to weed out duplicates. However, this code block:
allpartsincity(City):-
distinct((proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
part(Part, Name, Color, Num, X),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.
yields the following:
?- allpartsincity(london).
p2 bolt green 17 paris
p5 cam blue 12 paris
p2 bolt green 17 paris
p6 cog red 19 london
p5 cam blue 12 paris
true.
I'm not sure what I'm missing, but I'd appreciate if someone could point me in the right direction.
distinct/1 is a quite new predicate. It is only of relevance, if the incremental evaluation is of importance either because of infinite data or because (for some obscure reason) the exact order of answers is of relevance. OK, and maybe also because there are many redundant answers and the space to store them would be forbidding, but then a good setof/3 implementation might use a similar technique as well. In your case, you have just a data base of finitely many facts.
Since 19821, the classic predicate for your purpose is setof/3.
You did not give a minimal reproducible example. So I need to do some guessing. In any case, do trust the prolog-toplevel for printing.
city_part(City, CPart) :-
setof(t, city_part0(City, CPart), _).
city_part0(City, part(Part, Name, Color, Num, X)) :-
proj(Project, _A1, City),
sppj(_A2, Part, Project, _A3),
part(Part, Name, Color, Num, X).
You can avoid the intermediary predicate, but then the variable quantification will become cumbersome. I have given these variables already the names A1, A2, A3. These plus Project are only internal variables.
city_part(City, CPart) :-
setof(t, A1^A2^A3^Project^
( CPart = part(Part, Name, Color, Num, X),
proj(Project, A1, City),
sppj(A2, Part, Project, A3),
part(Part, Name, Color, Num, X)
), _).
As you wrote it, the goal part/5 that provides displayed values is unrelated to the conjunction you asked for in distinct/1. If I understand your problem correctly, most likely you should use distinct/2 instead. Try for instance
allpartsincity(City):-
distinct(part(Part, Name, Color, Num, X), (proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.

Prolog : Type error: `evaluable' expected, found `[]' (an empty_list)

I am trying to delete elements from a list which satisfy a given constraint. The user inputs a budget and items having price greater than the budget are deleted. I am getting a type error when consulting the file.
:- use_module(library(lists)).
main(_) :-
write('Enter budget'),
nl,
read(Budget),
write(Buget),
numlist(1,168,MasterList),
BudgetList is [],
apply_budget(MasterList, Budget, BudgetList),
write(BudgetList).
apply_budget([],_,_).
apply_budget([H | T], B, R) :-
price(H, Pr),
(Pr > B ->
apply_budget(T, B, R);
append(R, [H], L),
R is L,
apply_budget(T, B, R)
).
You've written this entirely as if this were an imperative language. You simply can't delete elements from a list in Prolog. In Prolog everything is immutable. If you've got a list then the list cannot be changed. You can only create new things. So if you want to delete elements from a list you would create a new list without the elements you want to delete.
Here's how you should write your apply_budget predicates:
apply_budget([],_,[]).
apply_budget([H|Ts],B,Rs) :- price(H, P), P > B, !, apply_budget(Ts,B,Rs).
apply_budget([H|Ts],B,[H|Rs]) :- apply_budget(Ts,B,Rs).
The first is the case when the have an empty list in then you don't care about the budget and an empty list should come out.
The second is the case when the value H is greater than B so we throw away H and recurse. The ! (cut) is there to prevent back-tracking so that the third predicate isn't tried if we have failure.
The third is the case when H is less than or equal to B. In this case we keep H by building (not deleting) it on to the output as [H|Rs].
The cut could have been removed by writing the code this way:
apply_budget([],_,[]).
apply_budget([H|Ts],B,Rs) :- price(H, P), P > B, apply_budget(Ts,B,Rs).
apply_budget([H|Ts],B,[H|Rs]) :- price(H, P), P =< B, apply_budget(Ts,B,Rs).
I think that becomes less manageable, but you can decide which you like better.

Program doesnt work. Prolog

I have some problem whith this code. The func3 was never invoked :
technology(board, saw, table).
technology(wood, sanded, board).
technology(water, grow, tree).
material(table, board, 20).
material(table, tree, 5).
material(wood, water, 100).
equipment(table,saw, cut, 10).
equipment(board, plane, polish, 7).
equipment(tree, watering, growing, 100).
specialization(saw, wood).
specialization(plane, wood).
specialization(watering, forestry).
plan_vypusku(table,10).
potreba_u_zahotovkah1(M, V):-
write(M + V),
nl,
    technology(F, _, M),
material(M, F, C),
Z is V * C,
write(F - Z),
nl.
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
potreba_u_zahotovkah(C, G):-
findall(X, specialization(X, C), XL),
write(XL),
nl,
plan_vypusku(G, S),
func2(XL, S).
Result:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
Help PLS!
I don't know what you're up to, but I have an explanation of the unexpected failure you observed.
The query you made wrote the following lines by side-effect (write/1 and nl/0) and then failed:
?- potreba_u_zahotovkah(wood,table).
[saw,plane]
InF2
[table]
false.
The highlighted line was output by the following highlighted write/1 and nl/0:
func2([A, B], C):-
write("InF2"),
nl,
findall(M, equipment(M, A, _, _), ML),
write(ML),
nl,
func3(ML, C),
func2(B, C).
So above variable ML was bound to [table] when the goal func3(ML, C) was called.
Looking at your definition of func3/2 the reason for failure becomes apparent:
func3([A, B], C):-
write("InF3"),
nl,
potreba_u_zahotovkah1(A, C),
func3(B, C).
The clause head of func3/2 demands that the first argument is a list having exactly two elements. The list [table], however, has exactly one element, not two!
As no more choicepoint are open, the goal potreba_u_zahotovkah(wood,table) fails.

How to find a path with limited depth in graph?

link(a, b).
link(a, c).
link(b, c).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
symlink(F1, F2) :-
link(F1, F2).
symlink(F1, F2) :-
link(F2, F1).
profile(a,box). %Tag it is the same as box for a.
find(Start, Tag, Rpath) :
find2(Start, Tag, 0, [], Rpath).
find2(Step, Tag, Count, Path, Rpath) :-
C is Count +1,
C < 5,
symlink(Step, A),
compat(A,Tag), % Compatible means the distance between the tag of A
% and the Tag that is given as argument should be maximum 1.
append(Path, [A|E],Rpath), %This part i want make my final path in Rpath.
not(member(Step,Path)),
find2(A, Tag, C, [Step|Path], Rpath).
You are quite close to a working predicate here. I've include a code snippet that hopefully solves a few of the small mistakes you make. Notice that find/3 is the predicate you would actually use (from the outside), a so-called wrapper clause.
find/4 works in the following way:
The first clause is only used to detect a transgression of the maximum depth.
The second clause is only used to detect a goal node, i.e. one that matches the given tag.
The third clause does the real job of finding a symmetric link in the graph.
Some small things to note:
Renamed symlink/2 to symmetric_link/2 to avoid confusion with symbolic links.
Used \+ instead of not for negation (the former is more common I believe).
Used tag/2 for tagging nodes, not profile/2 to avoid confusion with the act of profiling/analyzing code performance.
Code snippet:
link(a, b).
link(a, c).
link(b, c).
link(b, d).
link(b, e).
link(c, f).
link(c, g).
link(c, d).
tag(a, box).
symmetric_link(F1, F2) :-
link(F1, F2).
symmetric_link(F1, F2) :-
link(F2, F1).
maximum_depth(5).
find(Start, End, Path):-
find(Start, End, 0, [Start], Path).
find(_, _, Depth, _, _):-
maximum_depth(Max),
Depth > Max, !,
fail.
find(Node, Tag, _, _, [Node]):-
tag(Node, Tag), !.
find(Node1, Tag, Depth1, History, [Node1|Path]):-
symmetric_link(Node1, Node2),
\+ memberchk(Node2, History),
Depth2 is Depth1 + 1,
find(Node2, Tag, Depth2, [Node2|History], Path).
Example of usage:
?- find(g, box, Path).
Path = [g, c, d, b, a] ;
Path = [g, c, a] ;
Path = [g, c, b, a].
I have not fully tested this predicate and would advice you to write a unit test for these kinds of predicates. I use plUnit for this, which runs on SWI-Prolog and SICStus Prolog, but there may be others as well.
Hope this helps!

Querying each element of a list

I am doing homework for AI class and I am writing a prolog program.
I am supposed to take a list of names and check if each person in the list belongs to a specific country chosen.
what i have so far
% facts
person(bruce, australia, rhodri, bronwyn).
person(rhodri, newyork, dan, mary).
person(bronwyn, miami, gar, roo).
person(dan, miami, george, mimi).
person(mary, texas, mack, tiki).
person(gar, jamaica, zid, rem).
person(roo, newzealand, john, jill).
person(tom, mayday, dick, mel).
person(dick, newyork, harry, rin).
person(mel, miami, tom, stacey).
person(harry, miami, george, mimi).
person(rin, texas, mack, tiki).
person(tom, jamaica, zid, rem).
person(stacey, newzealand, john, jill).
% rules
eligible(P,C) :-
person(P, C, F, M) , !
; person(F, C, Newfather, Newmother), !
; person(M, C, Newfather, Newmother), !
; person(Newfather, C, Grandfather , Grandmother), !
; person(Newmother, C, Grandfather, Grandmother).
checkteam([] , C).
checkteam([H|T] , C) :- eligible(H, C) , checkteam(T, C).
the last two lines in particular i am having issues with, i am trying to test each member of the list with the eligible() function then let the first element of tail become the head and repeat.
I cant figure out a way to test each member and then display a fail if any of the members are not eligible or true if all members belong to that country.
Thanks in advance.
EDIT: was fooling around and changed the code a little, as for results
?- checkteam([bruce, dan], mayday).
true.
even though neither bruce or dan are from mayday or any parents or grandparents that do.
Your eligible predicate doesn't make sense to me (probably I am misunderstanding). But, if person is defined as person(Name, Country, Father, Mother) then it could be:
eligible(Name, Country) :- person(Name, Country, _, _).
eligible(Name, Country) :- person(Name, _, Father, _),
person(Father, Country, _, _).
eligible(Name, Country) :- person(Name, _, _, Mother),
person(Mother, Country, _, _).
Then your checkteam should still give you a warning. Put an underscore at the beginning of the variable name to get rid of it:
checkteam([], _Country).

Resources