AI-Prolog
view release on metacpan or search on metacpan
lib/AI/Prolog/Article.pod view on Meta::CPAN
gives('George Bush', grief, liberals).
gives('Bill Clinton', grief, conservatives).
Note that those are not function calls. Those are merely facts stating the
relationship of the arguments to one another. Additionally, "tom" and "book"
are each repeated. In Prolog, those each refer to the same entity.
Of course, facts can have a varying number of arguments, even for the same
functor. The following are all legal:
parent(bob, tim). % parent/2
parent(sue, alex, william). % parent/3
male(sue). % male/1
female(sue). % female/1
frobnitz. % frobnitz/0
You can name a predicate just about anything that makes sense, but note that
some predicates are built-in and their names are reserved. The document
C<AI::Prolog::Builtins> has a list of the predicates that C<AI::Prolog>
directly supports. If you're in the C<aiprolog> shell (explained later), you
can type C<help.> to get a list of built-in predicates and
C<help('functor/arity')> (e.g., C<help('consult/1')>) to get description of how
lib/AI/Prolog/Article.pod view on Meta::CPAN
Logic programming works by pushing these lists onto a stack and walking through
the stack and seeing if you can unify everything (sort of). But how to unify
from one item to the next? We assign names to the unknown values and see if
we can unify them. When we get to the next item in the stack, we check to see
if any named variables have been unified. If so, the engine will try to unify
them along with the other known variables.
That's a bad explanation, so here's how it works in Prolog. Imagine the
following knowledge base:
parent(sally, tom)
parent(bill, tom)
parent(tom, sue)
parent(alice, sue)
parent(sarah, tim)
male(bill)
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).
lib/AI/Prolog/Article.pod view on Meta::CPAN
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,
after you issue the query you can "trace" the execution:
$prolog->query('father(Who)');
$prolog->trace(1); # after the query, before the results
while ( my $result = $prolog->results ) {
...
Running the program again produces a lot of output, the beginning of which
matches our description of how logic programming works internally:
= Goals:
father(A)
==> Try: father(A) :-
parent(A, B),
male(A)
= Goals:
parent(A, C),
male(A)
==> Try: parent(sally, tom) :- null
= Goals:
male(sally)
==> Try: male(bill) :- null
<<== Backtrack:
= Goals:
male(sally)
==> Try: male(tom) :- null
<<== Backtrack:
lib/AI/Prolog/Article.pod view on Meta::CPAN
At this point, there's a good chance that you're thinking that you would just
stuff this into a relational database. Of course, this assumes you need
relational data and want to go to the trouble of setting up a database and
querying it from Perl. This is a good solution if you only need simple
relations. In fact, Prolog is often used with relational databases as the two
are closely related. SQL is a I<special purpose> declarative language whereas
Prolog is a I<general purpose> declarative language.
Firing up SQLite, let's create two tables and insert data into them.
sqlite> CREATE TABLE parent_2 (parent VARCHAR(32), child VARCHAR(32));
sqlite> CREATE TABLE male_1 (person VARCHAR(32));
sqlite> INSERT INTO parent_2 VALUES ('sally', 'tom');
sqlite> INSERT INTO parent_2 VALUES ('bill', 'tom');
sqlite> INSERT INTO parent_2 VALUES ('tom', 'sue');
sqlite> INSERT INTO parent_2 VALUES ('alice', 'sue');
sqlite> INSERT INTO parent_2 VALUES ('sarah', 'tim');
sqlite> INSERT INTO male_1 VALUES ('bill');
sqlite> INSERT INTO male_1 VALUES ('tom');
sqlite> INSERT INTO male_1 VALUES ('tim');
We can then find out who the fathers are with the following query.
SELECT parent
FROM parent_2, male_1
WHERE parent_2.parent = male_1.person;
This is very similar to Prolog but we are forced to explicitly state the
relationship. Many Prolog queries are conceptually similar to SQL queries but
the relationships are listed directly in the program rather than in the query.
When working with a relational database, we can get around this limitation with
a view:
CREATE VIEW father AS
SELECT parent
FROM parent_2, male_1
WHERE parent_2.parent = male_1.person;
And finding out who the fathers are is trivial:
SELECT * FROM father;
Further, databases, unlike many Prolog implementations, support indexing,
hashing, and reordering of goals to reduce backtracking. Given all of that,
why on earth would someone choose Prolog over SQL?
As stated earlier, Prolog is a general purpose programming language. Imagine
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
Omit the period after the number or put a zero after it:
X is 5.0 + 7.
X is 5 + 7.
Because numbers use Perl scalars, you may mix types (ints and floats) and they
will behave as you expect in Perl.
Precedence is C<*> and C</>, left to right, followed by C<+> and C<->, left to
right followed by C<%>, left to right. (I probably should change that.)
Naturally, parentheses may be used for grouping:
X is 3 * 5 + 2. % is(X, plus(mult(3, 5), 2)).
X is 3 * (5 + 2). % is(X, mult(3, plus(5, 2))).
When using math, note that C<is> is similar to Perl's assignment operator, C<=>.
This can be confusing.
X is 3 + 2.
Sets C<X> to the value of C<5>.
lib/AI/Prolog/Term.pm view on Meta::CPAN
sub to_data {
my $self = shift;
$self->{_results} = {};
# @results is the full results, if we ever need it
my @results = $self->_to_data($self);
return AsObject->new( $self->{_results} ), \@results;
}
sub _to_data {
my ( $self, $parent ) = @_;
if ( defined $self->{varname} ) {
# XXX here's where the [HEAD|TAIL] bug is. The engine works fine,
# but we can't bind TAIL to a result object and are forced to
# switch to raw_results.
my $varname = delete $self->{varname};
( $parent->{_results}{$varname} ) = $self->_to_data($parent);
$self->{varname} = $varname;
}
if ( $self->{bound} ) {
my $functor = $self->functor;
my $arity = $self->arity;
return $self->ref->_to_data($parent) if $self->{deref};
return [] if NULL eq $functor && !$arity;
if ( "cons" eq $functor && 2 == $arity ) {
my @result = $self->{args}[0]->_to_data($parent);
my $term = $self->{args}[1];
while ( "cons" eq $term->getfunctor && 2 == $term->getarity ) {
if ( $term->{varname} ) {
push @result => $term->_to_data($parent);
} else {
push @result => $term->getarg(0)->_to_data($parent);
}
$term = $term->getarg(1);
}
# XXX Not really sure about this one
push @result => $term->_to_data($parent)
unless NULL eq $term->getfunctor && !$term->getarity;
# ? "]"
# : "|" . $term->_to_data($parent) . "]";
return \@result;
}
else {
my @results = $self->functor;
if ( $self->arity ) {
#push @results => [];
my $arity = $self->arity;
my @args = @{ $self->args };
if (@args) {
for my $i ( 0 .. $arity - 1 ) {
push @results => $args[$i]->_to_data($parent);
}
# I have no idea what the following line was doing.
#push @results => $args[$arity - 1]->_to_data($parent)
}
}
return @results;
}
} # else unbound;
return undef;
}
my %varname_for;
my $varname = 'A';
lib/AI/Prolog/Term/Number.pm view on Meta::CPAN
my ($proto, $number) = @_;
my $self = $proto->SUPER::new($number, 0);
$self->{varid} = defined $number && looks_like_number($number)
? $number
: 0;
return $self;
}
sub value { shift->{varid} }
sub dup { # should this be recast as the parent?
my $self = shift;
return $self->new($self->{varid});
}
1;
__END__
=head1 NAME
t/35clause.t view on Meta::CPAN
'... and its to_string representation should reflect this';
$db = Parser->consult('p(this,that).');
$termlist = Parser->new('p(X,p(X,Y)).')->_termlist;
#$termlist->{definer}[0] = 'anything';
$termlist->resolve($db);
$termlist = Parser->new(<<"END_PROLOG")->_termlist;
father(Parent, Child) :-
male(Parent),
parent(Parent, Child).
END_PROLOG
$clause = $CLASS->new($termlist->term, $termlist->next);
is $clause->to_string, "father(A, B) :- \n\tmale(A),\n\tparent(A, B)",
'Building a complex clause should succeed';
t/80preprocessor_math.t view on Meta::CPAN
]), 'plus(mult(5, 4), 7.2)', '... and compound expressions should parse properly';
is $CLASS->_parse([
[qw/ ATOM 5 /],
[qw/ OP * /],
[qw/ LPAREN ( /],
[qw/ ATOM 4 /],
[qw/ OP + /],
[qw/ ATOM 7.2 /],
[qw/ RPAREN ) /],
]), 'mult(5, plus(4, 7.2))', '... and parentheses should group properly';
is $CLASS->process('X is 3 + 2.'), 'is(X, plus(3, 2)).',
'... and it should be able to transform full math expressions.';
my @expressions = (
'X is 3.',
'is(X, 3).',
'Answer = A + B.',
'eq(Answer, plus(A, B)).',
( run in 0.284 second using v1.01-cache-2.11-cpan-4d50c553e7e )