AI-Prolog

 view release on metacpan or  search on metacpan

lib/AI/Prolog/Article.pod  view on Meta::CPAN

 male(tom)
 male(tim)

Now let's assume we have a rule that states that someone is a father if they
are a parent and they are male.

 father(Person) :-
   parent(Person, _),
   male(Person).

In the above rule, the underscore is called an "anonymous vairable" and means
"I don't care what this value is."  Prolog may still bind the variable
internally (though this behavior is not guaranteed), but its value will not be
taken into account when trying to determine if terms unify.

Taking the first term in the rule, the logic engine might try to unify this
with the first fact in the knowledge base, C<parent(sally, tom)>. C<Person>
unifies with I<sally>.  The underscore, C<_>, unifies with I<tom> but since
we stated this unification is unimportant, we can ignore that.

We now have a fact which unifies with the first term in the rule, so we push
this information onto a stack.  Since there are still additional facts we can
try, we set a "choice point" in the stack telling us which fact we last tried.
If we have to backtrack to see a choice point, we move on to the next fact and
try again.

Moving on to the next term in the rule, C<male(Person)>, we know that "sally"
is unified to C<Person>, so we now try to unify C<male(sally)> with all of the
corresponding rules in the knowledge base. Since we can't, the logic engine
backs up to the last item where we could make a new choice and sees
C<parent(bill, tom)>. C<Person> gets unified with I<bill>. Then in moving to
the next rule we see that we unify with C<male(bill)>. Now, we check the first
item in the rule and see that it's C<father(Person)>. and the logic engine
reports that I<bill> is a father.

Note that we can then force the engine to backtrack and by continuously
following this procedure, we can determine who all the fathers are.

And that's how logic programming works. Simple, eh? (Well, individual items can
be lists or other rules, but you get the idea).

=head2 Executing Prolog in Perl

Getting back to Perl, how would we implement that in a Perl program?

The basic process for using C<AI::Prolog> looks something like this:

 use AI::Prolog;
 my $prolog = AI::Prolog->new($prolog_code);

Create a new C<AI::Prolog> object, passing Prolog code as the argument.  If you
prefer, you can wrap the constructor in a C<BEGIN> block:

 my $prolog;
 BEGIN {
   $prolog = AI::Prolog->new(<<'  END_PROLOG');
     % some Prolog code goes here
   END_PROLOG
 }

This is not strictly necessary, but if your Prolog code has a syntax error, it
will be a compile-time error, not a run-time error, and you'll get an error
message similar to:

 Unexpected character: (Expecting: ')'.  Got (.)) at line number 12.
 BEGIN failed--compilation aborted at test.pl line 7.

Note that the line number for "Unexpected character" is relative to the Prolog
code, not the Perl code.

After the contructor, issue your query:

 $prolog->query($some_query);

And do something with the results:

 while ( my $results = $prolog->results ) {
     print "@$results\n";
 }

Results are usually each returned as an array reference with the first argument
being the functor and subsequent arguments being the values.  If any value is a
list, it will be represented as an array reference.  We'll see more on that
later as we cover lists.

Now let's see the full program:

 #!/usr/bin/perl
 use strict;
 use warnings;
 use AI::Prolog;
 
 my $prolog;
 
 # If reading from DATA, we need a CHECK block to ensure that
 # DATA is available by the time the constructor is called
 CHECK {
   $prolog = AI::Prolog->new( do { local $/; <DATA> } );
 }
 
 $prolog->query( 'father(WHO).' );
 while ( my $results = $prolog->results ) {
     print "@$results\n";
 }
 
 __DATA__
 parent(sally, tom).
 parent(bill, tom).
 parent(tom, sue).
 parent(alice, sue).
 parent(sarah, tim).
 
 male(bill).
 male(tom).
 male(tim).
 
 father(Person) :-
     parent(Person, _),
     male(Person).

If you run this program, it will quite happily print out "father bill" and
"father tom."  In fact, if you really want to see what's going on internally,



( run in 1.160 second using v1.01-cache-2.11-cpan-13bb782fe5a )