prolog

Querying and Displaying hierarchical Prolog information


I am working with a Prolog database that has a set of entities with properties and values. I would like to output every entity along with each of its properties and values.

I'm very new to Prolog, and I have something that gives me the information I need, but it's not readable to look at and it also is likely pretty inefficient.

Primarily, I'm wondering how to display the information more nicely, but also any obvious ways to make it more efficient would be very helpful too.

Here is a toy version of my database:

% example objects
instanceOf(ball_1, physicalObject).
instanceOf(ball_2, physicalObject).
instanceOf(block_1, physicalObject).


% example properties
propertyOf(color_ball_1, ball_1).
propertyOf(size_ball_1, ball_1).

propertyOf(color_ball_2, ball_2).
propertyOf(size_ball_2, ball_2).

propertyOf(color_block_1, block_1).
propertyOf(size_block_1, block_1).


% example property values
hasValue(color_ball_1, red).
hasValue(size_ball_1, big).

hasValue(color_ball_2, blue).
hasValue(size_ball_2, small).

hasValue(color_block_1, red).
hasValue(size_block_1, small).

(Side note: I don't love the way this is stored, it feels clunky that I have color_ball_1 and color_ball_2 as opposed to anything that is a physicalObject just having a color, but I don't know how to go about doing that properly in Prolog.)

Here are my functors for getting all of the objects along with their properties and values:

% find all properties and values for one object
findEveryPropertyWithValue(Object, Properties) :-
    % get one object ...
    instanceOf(Object, physicalObject),

    % find every property of every object
    findall((Property, Value), (propertyOf(Property, Object), hasValue(Property, Value)), Properties).


% find all objects with properties and values
findAllObjects(Objects_and_Properties) :-
    % find every physicalObject with its Properties and Values
    findall((Obj, Properties), (instanceOf(Obj, physicalObject), findEveryPropertyWithValue(Obj, Properties)), Objects_and_Properties).

If I just run findEveryPropertyWithValue then I get an okay format, but it could be better displayed to show each property on its own line or something:

?- findEveryPropertyWithValue(Obj, Props).
Obj = ball_1,
Props = [(color_ball_1, red), (size_ball_1, big)] ;
Obj = ball_2,
Props = [(color_ball_2, blue), (size_ball_2, small)] ;
Obj = block_1,
Props = [(color_block_1, red), (size_block_1, small)].

But I need to ask Prolog to keep finding solutions manually, and I want them all at once, which is what findAllObjects is for, but the output is even less readable:

?- findAllObjects(Objs).
Objs = [(ball_1, [(color_ball_1, red), (size_ball_1, big)]), (ball_2, [(color_ball_2, blue), (size_ball_2, small)]), (block_1, [(color_block_1, red), (size_block_1, small)])].

How do I get a more readable output while still getting every solution in one call?

(And any notes on how to make the overall implementation better are very welcome!)


Solution

  • Logtalk (which you can run with most Prolog systems) provides straight-forward representation solutions. For example:

    :- category(physical_object).
    
        :- public(color/1).
        color(black).
    
        :- public(size/1).
        size(tiny).
    
    :- end_category.
    
    
    % example objects
    
    :- object(ball_1,
        imports(physical_object)).
    
        color(red).
        size(big).
    
    :- end_object.
    
    
    :- object(ball_2,
        imports(physical_object)).
    
        color(blue).
        size(small).
    
    :- end_object.
    
    
    :- object(block_1,
        imports(physical_object)).
    
        color(red).
        size(small).
    
    :- end_object.
    

    Notice that the physical_object category provides default values for the color and size properties. Other representation are possible, including more compact/space-efficient ones. It would help to know how many objects and how many properties per object for an informed choice.

    Query the objects is similar. For example:

    % find all properties and values for one object
    find_property_value_pairs(Object, Pairs) :-
        imports_category(Object, physical_object),
        findall(
            Property-Value,
            (   Object::current_predicate(Property/1),
                functor(Template, Property, 1),
                Object::Template,
                arg(1, Template, Value)
            ),
            Pairs
        ).
    
    
    % find all objects with properties and values
    find_all_object_property_pairs(Pairs) :-
        findall(
            Object-Properties,
            find_property_value_pairs(Object, Properties),
            Pairs
        ).
    

    Note, however, that querying and displaying of solutions is orthogonal. For example:

    ?- forall(find_property_value_pairs(Object, Pairs), format('~q - ~q~n', [Object, Pairs])).
    ball_1 - [color-red,size-big]
    ball_2 - [color-blue,size-small]
    block_1 - [color-red,size-small]
    true.