NAME
AI::Prolog - Perl extension for logic programming.
SYNOPSIS
use AI::Prolog;
use Data::Dumper;
my $database = <<'END_PROLOG';
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
my $logic = AI::Prolog->new($database);
$logic->query('append(X,Y,[a,b,c,d])');
while (my $result = $logic->results) {
print Dumper($result->X);
print Dumper($result->Y);
}
ABSTRACT
AI::Prolog is merely a convenient wrapper for a pure Perl Prolog compiler.
Regrettably, at the current time, this requires you to know Prolog. That will
change in the future.
EXECUTIVE SUMMARY
In Perl, we traditionally tell the language how to find a solution. In logic programming, we describe what a solution would look like and let the language find it for us.
QUICKSTART
For those who like to just dive right in, this distribution contains a simple Prolog shell called aiprolog
and a short adventure game called spider.pro
.
See the bin/
and data/
directories in the distribution.
DESCRIPTION
AI::Prolog
is a pure Perl predicate logic engine. In predicate logic, instead of telling the computer how to do something, you tell the computer what something is and let it figure out how to do it. Conceptually this is similar to regular expressions.
my @matches = $string =~ /XX(YY?)ZZ/g
If the string contains data that will satisfy the pattern, @matches
will contain a bunch of "YY" and "Y"s. Note that you're not telling the program how to find those matches. Instead, you supply it with a pattern and it goes off and does its thing.
To learn more about Prolog, see Roman Barták's "Guide to Prolog Programming" at http://kti.ms.mff.cuni.cz/~bartak/prolog/index.html. Amongst other things, his course uses the Java applet that AI::Prolog
was ported from, so his examples will generally work with this module.
Fortunately, Prolog is fairly easy to learn. Mastering it, on the other hand, can be a challenge.
USING AI::Prolog
There are three basic steps to using AI::Prolog
.
For quick examples of how that works, see the examples/
directory with this distribution. Feel free to contribute more.
Creating a logic program
This module is actually remarkable easy to use. To create a Prolog program, you simply pass the Prolog code as a string to the constructor:
my $prolog = AI::Prolog->new(<<'END_PROLOG');
steals(PERP, STUFF) :-
thief(PERP),
valuable(STUFF),
owns(VICTIM,STUFF),
not(knows(PERP,VICTIM)).
thief(badguy).
valuable(gold).
valuable(rubies).
owns(merlyn,gold).
owns(ovid,rubies).
knows(badguy,merlyn).
END_PROLOG
Side note: in Prolog, programs are often referred to as databases.
Creating a query
To create a query for the database, use query
.
$prolog->query("steals(badguy,X)");
Running a query
Call the results
method and inspect the results
object:
while (my $result = $prolog->results) {
printf "badguy steals %s\n", $results->X;
}
GRAMMAR
See AI::Prolog::Builtins for the grammar and built in predicates.
CLASS METHODS
new($program)
This is the constructor. It takes a string representing a Prolog program:
my $prolog = AI::Prolog->new($program_text);
See AI::Prolog::Builtins and the examples/
directory included with this distribution for more details on the program text.
Returns an AI::Prolog
object.
trace([$boolean])
One can "trace" the program execution by setting this property to a true value before fetching engine results:
AI::Prolog->trace(1);
while (my $result = $engine->results) {
# do something with results
}
This sends trace information to STDOUT
and allows you to see how the engine is trying to satify your goals. Naturally, this slows things down quite a bit.
Calling trace
without an argument returns the current trace
value.
raw_results([$boolean])
Ordinarily, the object returned by query
will provide methods to allow you to access the data the variables are bound to. However, this is not always sufficient. You can get access to the full, raw results by setting raw_results
to true. In this mode, the results are returned as an array reference with the functor as the first element and an additional element for each term. Lists are represented as array references.
AI::Prolog->raw_results(1);
$prolog->query('steals(badguy, STUFF, VICTIM)');
while (my $r = $prolog->results) {
# do stuff with $r in the form:
# ['steals', 'badguy', $STUFF, $VICTIM]
}
Calling raw_results
without an argument returns the current raw_results
value.
INSTANCE METHODS
do($query_string)
This method is useful when you wish to combine the query()
and results()
methods but don't care about the results returned. Most often used with the assert(X)
and retract(X)
predicates.
$prolog->do('assert(loves(ovid,perl))');
This is a shorthand for:
$prolog->query('assert(loves(ovid,perl))');
1 while $prolog->results;
This is important because the query()
method merely builds the query. Not until the results()
method is called is the command actually executed.
query($query_string)
After instantiating an AI::Prolog
object, use this method to query it. Queries currently take the form of a valid prolog query but the final period is optional:
$prolog->query('grandfather(Ancestor, julie)');
This method returns $self
.
results
After a query has been issued, this method will return results satisfying the query. When no more results are available, this method returns undef
.
while (my $result = $prolog->results) {
printf "%s is a grandfather of julie.\n", $result->Ancestor;
}
If raw_results
is false (this is the default behavior), the return value will be a "result" object with methods corresponding to the variables. This is currently implemented as a Hash::AsObject so the caveats with that module apply.
$logic->query('steals("Bad guy", STUFF, VICTIM)');
while (my $r = $logic->results) {
print "Bad guy steals %s from %s\n", $r->STUFF, $r->VICTIM;
}
See raw_results
for an alternate way of generating output.
BUGS
A query using [HEAD|TAIL]
syntax does not bind properly with the TAIL
variable when returning a result object. You will need to restructure your query to avoid this syntax or set raw_results
to true and parse the results yourself.
See AI::Prolog::Builtins and AI::Prolog::Engine for known bugs and limitations. Let me know if (when) you find them. See the built-ins TODO list before that, though.
TODO
Find the
HEAD|TAIL
bug.See BUGS
Fix infinite loop.
perl examples/path.pl 3
That never returns or I haven't run it long enough.
Support for more builtins.
Performance improvements.
I have a number of ideas for this, but it's pretty low-priority until things are stabilized.
Anonymous variables.
father(Person, _). % is Person a father of *anyone*?
We can't use the underscore. We have to make up a dummy variable and use it only once.
father(Person, Dummy1).
Ugh.
Add "sugar" interface.
Better docs.
Tutorial.
Data structure cookbook.
EXPORT
None by default. However, for convenience, you can choose ":all" functions to be exported. That will provide you with Term
, Parser
, and Engine
classes. This is not recommended and most support and documentation will now target the AI::Prolog
interface.
If you choose not to export the functions, you may use the fully qualified package names instead:
use AI::Prolog;
my $database = AI::Prolog::Parser->consult(<<'END_PROLOG');
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
my $query = AI::Prolog::Term->new("append(X,Y,[a,b,c,d]).");
my $engine = AI::Prolog::Engine->new($query,$database);
while (my $result = $engine->results) {
print "$result\n";
}
SEE ALSO
W-Prolog: http://goanna.cs.rmit.edu.au/~winikoff/wp/
X-Prolog: http://www.iro.umontreal.ca/~vaucher/XProlog/
Roman Barták's online guide to programming Prolog: http://kti.ms.mff.cuni.cz/~bartak/prolog/index.html
AUTHOR
Curtis "Ovid" Poe, <moc tod oohay ta eop_divo_sitruc>
Reverse the name to email me.
This work is based on W-Prolog, http://goanna.cs.rmit.edu.au/~winikoff/wp/, by Dr. Michael Winikoff. Many thanks to Dr. Winikoff for granting me permission to port this.
Many features also borrowed from X-Prolog http://www.iro.umontreal.ca/~vaucher/XProlog/ with Dr. Jean Vaucher's permission.
COPYRIGHT AND LICENSE
Copyright 2005 by Curtis "Ovid" Poe
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.