AI-Prolog

 view release on metacpan or  search on metacpan

data/spider.pro  view on Meta::CPAN


describe(cave_entrance) :-
        print('You are in the mouth of a dank cave.  The exit is to'), nl,
        print('the south; there is a large, dark, round passage to'), nl,
        print('the east.'), nl.

describe(cave) :-
        alive(spider),
        at(ruby, in_hand),
        print('The spider sees you with the ruby and attacks!!!'), nl,
        print('    ...it is over in seconds....'), nl,
        die.

describe(cave) :-
        alive(spider),
        print('There is a giant spider here!  One hairy leg, about the'), nl,
        print('size of a telephone pole, is directly in front of you!'), nl,
        print('I would advise you to leave promptly and quietly....'), nl.

describe(cave) :-
        print('Yecch!  There is a giant spider here, twitching.'), nl.

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


 Welcome to AI::Prolog v 0.732
 Copyright (c) 2005, Curtis "Ovid" Poe.
 AI::Prolog comes with ABSOLUTELY NO WARRANTY.  This library is free software;
 you can redistribute it and/or modify it under the same terms as Perl itself.

 Type '?' for help.

 ?- consult('gives.pro').

The second notation allows you to consult multiple files and add all of them to
the knowledge base.

After issuing the C<consult/1> command, the shell will appear to hang.  Hit
I<Enter> to continue.  We'll explain this behavior in a moment.

Now that you've loaded the program into the shell, issue the following query:

 ?- gives(X,Y,Z).

The shell should respond:

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


 %who_loves = reverse %loves;
 $who       = $who_loves{perl};

(C<AI::Prolog>, being written in Perl, is slow.  So the C<O(n)> versus
C<O(1)> argument doesn't hold.  Versions written in C are far more practical in
this regard.)

But this fails, too.  The first problem is that we have duplicated data.  If we
have to add additional data to the C<%loves> hash, we'll have to remember to
synchronize the hashes.  The second problem is that Perl is just a little too
popular:

 loves(ovid,    perl).
 loves(alice,   perl).
 loves(bob,     perl).
 loves(charlie, perl).

If we simply reverse the hash for those entries, we lose three of those names.
So we have to play with this some more.

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


Now if Alice owns things, how do we find out if she owns cats?  First, we need
to define a predicate that succeeds if a given term is an element of a given
list.

 member(X, [ X | _ ]).
 member(X, [ _ | TAIL ]) :-
    member(X, TAIL).

The first clause in this predicate states that C<X> is a member of a list if
it's the head of a list.  The second clause states that C<X> is a member of a
list if it's a member of the tail of the list.  Here's how this works out:

 ?- member(3, [1,2,3,4]).

Prolog checks the first clause and sees that 3 is not the head of the list:
C<member(3, [1|2,3,4])>.  It then checks to see if it's a member of the tail of
the list: C<member(3, [2|3,4])>.  Again this fails so Prolog tries again and
we succeed:  C<member(3, [3|4])>.

So how do we see if Alice has cats?

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


This book is sometimes referred to simply as "Clocksin/Mellish".  First
published in the early 80s, this is the book that brought Prolog into the
mainstream.

=item The Art of Prolog

htp://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=8327

This excellent MIT textbook is very in-depth and covers "proving" Prolog
programming, second-order logic, grammars, and many working examples.

=back

=head1 Credits

Many thanks to Rebekah Golden, Danny Werner and David Wheeler for their
excellents comments and insights.

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


 gather([2,4], WhichList, [x,y]).

This query, when executed in the C<aiprolog> shell will output a response
similar to this:

 gather([2,4], [A,x,B,y|C], [b,d]).

When examining this, we see that the first, third, and fifth elements (and
beyond) of the list are variables.  Unfortunately, as an infinite number of
lists will satisfy this goal, attempting to fetch the a second result from the
same query will result in an infinite loop.

=head2 Determine the intersection of two lists.

Usage: C<intersection(List1, List2, Intersection).>

This definition depends on the C<member/2> predicate defined in this document.

 intersection([H|T], L, [H|U]) :-
    member(H,L),
    intersection(T,L,U).
 intersection([_|T], L, U) :-
    intersection(T,L,U).
 intersection(_,_,[]).

The C<intersection/3> predicate will compute the intersection of two lists.
You probably only want the first result from this predicate.  See C<trace/0>
to understand why it returns more than one intersection.

=head2 Reverse a list.

Usage:  C<reverse(List, ReversedList).>

 reverse(List, Reverse) :-
    reverse_accumulate(List, [], Reverse).
 reverse_accumulate([], List, List).
 reverse_accumulate([Head|Tail], Accumulate, Reverse) :-
    reverse_accumulate(Tail, [Head|Accumulate], Reverse).

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

 reverse([],[]).
 reverse([Head|Tail], Reverse) :- 
    reverse(Tail, ReverseTail), 
    append(ReverseTail, [Head], Reverse).

For this, you take the tail of the list, reverse it and append the head of the
list to it.  However, this runs in C<O(N^2)>.  This runs so slowly that
reversing a 30 element list takes 496 logical inferences.  As a result, the
naive reverse is frequently used as a benchmarking tool for logic programs.

If reversing a 30 element list via the naive reverse takes .1 seconds, we can
say that the Prolog implementation is running at about 5000 logical inferences
per second.  This is known by the unfortunate acronym of LIPS, the standard
measure of the speed of logic programs.  Modern Prolog implementations
frequently measure their performance in MLIPS, or MegaLIPS.  By contrast, the
human mind is frequently estimated to run between 1 to 4 LIPS.  This
demonstrates that there's much more to cognition than logic.

=head2 Checking if a list is a subset of another list.

Usage:  C<subset(Subset, List).>

This definition depends on the C<member/2> predicate defined in this document.

lib/AI/Prolog/Engine.pm  view on Meta::CPAN

L<AI::Prolog::Builtins|AI::Prolog::Builtins> documentation.

This documentation is provided for completeness.  You probably want to use
L<AI::Prolog|AI::Prolog>.

=head1 CLASS METHODS

=head2 C<new($query, $database)>

This creates a new Prolog engine.  The first argument must be of type
C<AI::Prolog::Term> and the second must be a database created by
C<AI::Prolog::Parser::consult>.

 my $database = Parser->consult($some_prolog_program);
 my $query    = Term->new('steals(badguy, X).');
 my $engine   = Engine->new($query, $database);
 Engine->formatted(1);
 while (my $results = $engine->results) {
    print $results, $/;
 }

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


(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
(similar to how recursive functions in Perl generally should have a terminating
condition defined in them.)

The second line is where the bulk of the work gets done. In Prolog, to identify
the head (first element) of a list and its tail (all elements except the
first), we use the syntax [head|tail]. Since ":-" is read as "if" in Prolog,
what this says if we want to concatenate (a,b,c) and (d,e,f):

=over 4

=item * Given a list with a head of W and a tail of X:

 @list1 = qw/a b c/; (qw/a/ is W, the head, and qw/b c/ is X, the tail)

t/50engine.t  view on Meta::CPAN

    writeln/1
};

@keys = sort keys %{$database->ht};
is_deeply \@keys, \@expected,
    '... and the basic prolog terms should be bootstrapped';
can_ok $engine, 'results';
is $engine->results, 'append([], [a,b,c,d], [a,b,c,d])',
    '... calling it the first time should provide the first unification';
is $engine->results, 'append([a], [b,c,d], [a,b,c,d])',
    '... and then the second unification';
is $engine->results, 'append([a,b], [c,d], [a,b,c,d])',
    '... and then the third unification';
is $engine->results, 'append([a,b,c], [d], [a,b,c,d])',
    '... and then the fifth unification';
is $engine->results, 'append([a,b,c,d], [], [a,b,c,d])',
    '... and then the last unification unification';
ok ! defined $engine->results,
    '... and it should return undef when there are no more results';

my $bootstrapped_db = clone($database);

t/70builtins.t  view on Meta::CPAN

is $prolog->results, 'loves(sally, A)',
    '... and the asserted fact should remain unchanged.';

$prolog->do("retract(loves(ovid,perl)).");
$prolog->query("loves(ovid,X)");
ok ! $prolog->results,
    "retract(X) should remove a fact from the database";

my @test_me;
sub test_me {
    my ( $first, $second, $third, $fourth ) = @_;
    @test_me = ( "\L$first", "\U$second", "\u$third", $fourth );
    return;
}
$prolog->query(q{perlcall2( "test_me", ["FIND ME","and me","also me", 42] ).});
ok $prolog->results, 'Called a perl function ok';
is_deeply \@test_me, ["find me","AND ME", "Also me", 42],
    'Perl function got results ok';

@test_me = ();
$prolog->query(q{perlcall2( X, ["Uh..."] ).});
ok ! $prolog->results, "Didn't call an unknown perl function";

t/80math.t  view on Meta::CPAN

    '... but trying to call is(7,X) with an unbound rhs should die';

$prolog->query('is(7,7)');
is $prolog->results, 'is(7, 7)', '... but it should succeed if both terms are bound and equal';

$prolog->query('is(5,7)');
ok ! defined $prolog->results, '... and it should fail if both terms are bound but unequal';

$prolog->query('gt(4,3)');
is $prolog->results, 'gt(4, 3)',
    'gt(X,Y) should succeed if the first argument > the second argument.';

$prolog->query('gt(3,34)');
ok ! $prolog->results,
    '... and it should fail if the first argument < the second argument.';

$prolog->query('gt(3,3)');
ok ! $prolog->results,
    '... and it should fail if the first argument = the second argument.';
    
$prolog->query('steals(badguy, X)');
is $prolog->results, 'steals(badguy, rubies)',
    '... and it should succeed as part of a complicated query';
ok ! $prolog->results, '... but it should not return more than the correct results';

$prolog->query('ge(4,3)');
is $prolog->results, 'ge(4, 3)',
    'ge(X,Y) should succeed if the first argument > the second argument.';

$prolog->query('ge(3,34)');
ok ! $prolog->results,
    '... and it should fail if the first argument < the second argument.';

$prolog->query('ge(3,3)');
is $prolog->results, 'ge(3, 3)',
    '... and it should succeed if the first argument = the second argument.';
    
$prolog->query('lt(3,4)');
is $prolog->results, 'lt(3, 4)',
    'lt(X,Y) should succeed if the first argument < the second argument.';

$prolog->query('lt(34,3)');
ok ! $prolog->results,
    '... and it should fail if the first argument < the second argument.';

$prolog->query('lt(3,3)');
ok ! $prolog->results,
    '... and it should fail if the first argument = the second argument.';

$prolog->query('le(3,4)');
is $prolog->results, 'le(3, 4)',
    'le(X,Y) should succeed if the first argument < the second argument.';

$prolog->query('le(34,3)');
ok ! $prolog->results,
    '... and it should fail if the first argument < the second argument.';

$prolog->query('le(3,3)');
is $prolog->results, 'le(3, 3)',
    '... and it should succeed if the first argument = the second argument.';

$prolog->query('is(X,plus(3,4))');
is $prolog->results, 'is(7, plus(3, 4))', 'plus/2 should succeed';

$prolog->query('is(X,plus(3,-4))');
is $prolog->results, 'is(-1, plus(3, -4))', '... even with negative values';

$prolog->query('is(X,plus(3,plus(-2,4)))');
is $prolog->results, 'is(5, plus(3, plus(-2, 4)))', '... or complicated math';

t/99regression.t  view on Meta::CPAN

is $prolog->results, 'i_am_at(bottom)', '... and the goal should be retracted';
ok ! $prolog->results, '... and we should not have spurious results';

$prolog = Prolog->new(<<'END_PROLOG');
    member_of(X, [X|_]).
    member_of(X, [_|Tail]) :-
        member_of(X, Tail).

    balls([a,b,c]).

    no_intersect([], _).
    no_intersect([Head|Tail], List) :-
        not(member_of(Head, List)),
        no_intersect(Tail, List).

    unique([]).
    unique([Head|Tail]) :-
        no_intersect([Head], Tail),
        unique(Tail).

    set_of_balls(A,B) :-
        balls(Balls),
        member_of(A, Balls),
        member_of(B, Balls),
        unique([A,B]).
END_PROLOG
$prolog->query('set_of_balls(X,Y).');
my @results;



( run in 0.723 second using v1.01-cache-2.11-cpan-39bf76dae61 )