0
votes

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.

2
You could query ?- leafes(X). Variables that occur in queries are existentially quantified, i.e. "Is there something that is a 'leafes'?" If you're not interested in the particular answer you can also use _ i.o. X.Wouter Beek
I already know this. Question was: when user prompts "leafes(any)", prolog gives "leafes(leafed); leafes(coniferous)".Flame
That's not really proper use of Prolog. When you query leafes(any) you are asking about one fact with no variables. leafes(Any) is really the correct way to query it. Your example is also an infinite loop. It doesn't show "false" but infinitely loops with successive "true" results.lurker

2 Answers

0
votes

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

0
votes

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