view release on metacpan or search on metacpan
0.63 Wed May 04, 2005
Allow parser to properly handle positive and negative numbers
and decimal points.
Added pow(X,Y).
Eliminated studlyCaps methods.
Added trace. and notrace.
Added a TODO test for a failing regression test.
0.62 Fri Feb 25, 2005
Added code to avoid bug in some Perl's where "undef" looks
like a number.
Major performance improvement. Now runs about 40% faster.
Added
listing.
println(X).
is(X,Y).
plus(X,Y).
minus(X,Y).
mult(X,Y).
div(X,Y).
lib/AI/Prolog/Article.pod view on Meta::CPAN
unlike imperative, objective, and functional styles, Perl does not have direct
support for logic programming. There is, however, much interest in bringing
logic programming to Perl 6. With luck the information presented here will not
be merely theoretical.
Logic programming is not as alien as programmers might think. Regular
expressions, SQL and grammars are all closely related to logic programming.
The shared component of these seemingly disparate technologies is how they
I<describe> their goals rather than state how to achieve it. This is the
essence of logic programming. Rather than tell the computer how to achieve a
given goal, we tell the computer what the goal looks like and let it figure out
how to get there. Because of this, some refer to logic programming as
"specification-based programming" because the specification and the program are
one and the same.
This article will focus on C<AI::Prolog>. Prolog, though not a "pure" logic
programming language, is the most widely used in the field. C<AI::Prolog> is a
Prolog engine written entirely in Perl and it's very easy to install and use.
However, if you start doing serious work in Prolog, I strongly recommend you
take a look at Salvador FandiE<241>o's C<Language::Prolog::Yaswi>. This module
allows access to SWI-Prolog (http://www.swi-prolog.org/) from within Perl.
lib/AI/Prolog/Article.pod view on Meta::CPAN
Now that we have a rough understanding of facts and rules, we need to know how
to get answers from them. Queries are typically entered in the "interactive
environment." This would be analogous to the query window in a database GUI.
With C<AI::Prolog>, a shell named C<aiprolog> is included for demonstration
purposes though we'll mainly focus on calling Prolog from within a Perl
program.
?- gives(tom, book, SOMEONE).
This looks just like a fact, but with some minor differences. The C<?-> at the
beginning of the query is a query prompt seen in interactive environments.
You'll also note that C<SOMEONE> is capitalized, making it a variable (only the
first letter needs to capitalized.) Thus, this query is asking who Tom will
gives books to.
?- gives(WHO, book, SOMEONE).
This query looks like the previous one, but since the first argument is
capitalized, we're asking "who will give books to whom?".
?- gives(WHO, WHAT, WHOM).
Finally, because all arguments are variables, we're asking for everyone who
will give anything to anybody.
Note that no code changes are necessary for any of this. Because Prolog facts
and rules define relationships between things, Prolog can automatically infer
additional relationships if they are logically supported by the program.
lib/AI/Prolog/Article.pod view on Meta::CPAN
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');
lib/AI/Prolog/Article.pod view on Meta::CPAN
append([HEAD|X], Y, [HEAD|Z]) :-
append(X, Y, Z).
You'll probably want to put that in a file named I<append.pro> and in the
shell C<consult('append.pro')> to follow along with some of the examples
you're about to see.
Explaining how that works would take a bit of time, so I recommend you work it
out on your own. Instead, I'll focus on its implications.
It looks like a lot of work to define how to append two lists. Perl is much
simpler:
my @Z = (@X, @Y);
In fact, that hides quite a bit of complexity for us. Anyone who has had to
concatenate two arrays in C can appreciate the simplicity of the Perl approach.
So what would the complexity of the C<append/3> predicate gain us? Naturally,
we can use this to append two lists. (The following output is similar to what
one would see in regular Prolog systems. It's been reproduced here for
clarity.)
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
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>.
If C<X> is already instantiated, this goal succeeds if the value of C<X> is the
value of the result of the right-hand side of the equation. Internally, if X is not
instantiated, it looks like this:
is(5, plus(3,2)).
The C<=> operator tries to unify the left-hand side with the right-hand side:
X = 3 + 2.
If C<X> is already instantiated, this goal succeeds if the value of C<X> is the
same goal as the right-hand side of the equation. Internally, if X is not
instantiated, it looks like this:
eq(plus(3,2), plus(3,2)).
When you first start using Prolog, you probably was C<is> instead of C<=>.
Logical comparisons are straightforward:
3 >= X.
Y > (4 + 3) * X.
X == Y. % a test for equality
lib/AI/Prolog/Engine.pm view on Meta::CPAN
package AI::Prolog::Engine;
$REVISION = '$Id: Engine.pm,v 1.13 2005/08/06 23:28:40 ovid Exp $';
$VERSION = '0.4';
use strict;
use warnings;
use Carp qw( confess carp );
use Scalar::Util qw/looks_like_number/;
use Hash::Util 'lock_keys';
use aliased 'AI::Prolog::Term';
use aliased 'AI::Prolog::Term::Cut';
use aliased 'AI::Prolog::Term::Number';
use aliased 'AI::Prolog::TermList';
use aliased 'AI::Prolog::TermList::Step';
use aliased 'AI::Prolog::TermList::Primitive';
use aliased 'AI::Prolog::KnowledgeBase';
use aliased 'AI::Prolog::Parser';
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
## no critic (RcsKeywords,PodSections,InterpolationOfMetachars,EmptyQuotes,ConstantPragma,InitializationForLocalVars,LocalVars,PunctuationVars)
package AI::Prolog::Engine::Primitives;
$REVISION = '$Id: Primitives.pm,v 1.1 2005/08/06 23:28:40 ovid Exp $';
$VERSION = '0.3';
use strict;
use warnings;
use base 'AI::Prolog::Engine';
use Scalar::Util 'looks_like_number';
use aliased 'AI::Prolog::Term';
use aliased 'AI::Prolog::Term::Cut';
use aliased 'AI::Prolog::Term::Number';
use aliased 'AI::Prolog::TermList';
use aliased 'AI::Prolog::TermList::Step';
use aliased 'AI::Prolog::ChoicePoint';
my %DESCRIPTION_FOR;
my $LONGEST_PREDICATE = '';
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
'Trace ' . ( $self->{_trace} ? 'ON' : 'OFF' ) );
return CONTINUE;
};
$PRIMITIVES[15] = sub { # is/2
my ( $self, $term, $c ) = @_;
my $rhs = $term->getarg(0)->deref;
my $lhs = $term->getarg(1)->value;
if ( $rhs->is_bound ) {
my $value = $rhs->value;
if ( not looks_like_number($value) ) {
return FAIL;
}
return $value == $lhs;
}
$rhs->bind( Number->new($lhs) );
push @{ $self->{_stack} } => $rhs;
return CONTINUE;
};
$PRIMITIVES[16] = sub { # gt/2
lib/AI/Prolog/Introduction.pod view on Meta::CPAN
In Perl, generally you can append one list to another with this:
my @Z = (@X, @Y);
However, that's telling the language what to do. As sentient beings, we can
look at that and infer more information. Given C<@Z> and C<@X>, we could infer
C<@Y>. Given just C<@Z>, we could infer all combinations of C<@X> and C<@Y>
that can be combined to form C<@Z>.
Perl cannot do that. In logic programming, however, by defining what
C<append()> looks like, we get all of that other information.
In Prolog, it looks like this:
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
(There's actually often something called a "cut" after the first definition,
but we'll keep this simple.)
What the above code says is "appending an empty list to a non-empty list yields
the non-empty list." This is a boundary condition. Logic programs frequently
require a careful analysis of boundary conditions to avoid infinite loops
lib/AI/Prolog/Introduction.pod view on Meta::CPAN
append(X, [b,c,d], [a,b,c,d]).
And finally, given Z, we can infer all X and Y that combine to form Z.
append(X,Y,[a,b,c,d]).
Note that you get all of that from one definition of how to append two lists.
You also don't have to tell the program how to do it. It just figures it out
for you.
Translating all of this into C<AI::Prolog> looks like this:
use AI::Prolog;
use Data::Dumper;
my $prolog = AI::Prolog->new(append_prog());
$prolog->raw_results(0) # Disable raw results
$prolog->query("append(X,Y,[a,b,c,d])");
while (my $result = $prolog->results) {
print Dumper($result->X); # array references
print Dumper($result->Y);
lib/AI/Prolog/Term/Number.pm view on Meta::CPAN
package AI::Prolog::Term::Number;
$REVISION = '$Id: Number.pm,v 1.3 2005/02/28 02:32:11 ovid Exp $';
$VERSION = '0.1';
@ISA = 'AI::Prolog::Term';
use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
use aliased 'AI::Prolog::Term';
sub new {
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});
lib/AI/Prolog/TermList/Primitive.pm view on Meta::CPAN
package AI::Prolog::TermList::Primitive;
$REVISION = '$Id: Primitive.pm,v 1.2 2005/02/20 18:27:55 ovid Exp $';
$VERSION = '0.1';
@ISA = 'AI::Prolog::TermList';
use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
sub new {
my ($class, $number) = @_;
my $self = $class->SUPER::new; # correct?
$self->{ID} = looks_like_number($number) ? $number : 0;
return $self;
}
sub ID { shift->{ID} }
sub to_string { " <".shift->{ID}."> " }
1;
__END__