view release on metacpan or search on metacpan
12345678910111213141516171819202122Revision history
for
Perl extension AI::Prolog.
0.740 2008-11-12
t/60aiprolog.t fixed number of planned tests (RT
#41154).
Fixed dependencies in some tests.
Fixed HEAD|TAIL results bug. Added test (RT
#64942).
(Should raw_results(0) be
default
? or keep backward compat?).
0.740 2008-11-12
t/60aiprolog.t loads modules in the right order now
0.739 2007-05-04
Explicitly
import
Carp. This fixes a bug exposed by
warnings.pm. Thanks to Andreas Koenig
for
sending in the failing test.
0.738 2007-05-01
Several test modules are now optional by virtue of a patch by
educated_foo.
0.737 2007-03-21
3940414243444546474849505152535455565758596061626364656667686970717273747576777879808182830.735_01 2006-07-31
Added perlcall2/2.
TODO: need ground/1 to constrain the 2nd arg to perlcall2/2.
Added Scalar::Util to the pre-reqs. It was already required.
Used Hash::Util::lock_keys to
lock
down object internals access.
First release by JJORE.
0.734 2006-04-05
Added minimum requirement of ExtUtils::MakeMaker 6.30 due to bug report
filed on RT. Thanks to zby
for
the
catch
.
Added a copy of the Perl Review article on logic programming. Thanks to
brian d
for
and the Perl Review
for
allowing me to republish this
article.
0.733 2005-10-08
Added POD tests (
sort
of)
Added strict to AI::Prolog
Converted to Module::Build
0.732 Sat August 6, 2005
Added lib/AI/Prolog/Cookbook.pm to the MANIFEST (whoops)
0.73 Sat August 6, 2005
Added AI::Prolog::Cookbook as an introduction to common problems
encountered
when
working
with
Prolog.
TermList->to_string now causes listings to look very similar to
SWI-Prolog. Much easier to
read
.
Term->to_string now identifies variables by letter, not /_\d+/.
Completed decoupling of AI::Prolog::Parser from the engine.
0.72 Sun July 31, 2005
Move builtins to their own class, AI::Prolog::Engine::Builtins
Non-existent predicates now issue a warning even
if
trace is off.
Added new predicates:
halt/0 -- currently a
no
-op outside of the aiprolog shell
help/0 -- list all builtin predicates
for
which help is available
help/1 -- list help
for
builtin predicate
write
/1
writeln/1
Added ** operator (pow/1) to math preprocessor. Leaving it out
earlier was an oversight.
0.71 Sat June 25, 2005
Removed _failgoal in Engine.pm. We now backtrack
if
we need
new results.
919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
Added data/sleepy.pro, a game that wasn't working because of
the retract/1 bug.
0.7 Mon June 20, 2005 (Happy Birthday to me)
Parsing errors now attempt to report the line number of the
error.
Added new predicates:
consult/1
listing/1
ne/2
Updated the license information display
for
aiprolog shell.
Modified listing/0 to not show builtins.
Added math preprocessor. This transforms proper Prolog math
into internal predicates the parser recognizes:
X is N + 1. % is(X, plus(N, 1)).
Pushed all parsing back to the Parser class. No more parsing
in TermList and Term :)
Added anonymous variables: foo(bar, _, baz). I don't have
any efficiency gains from this (yet) because they're just
variables
with
hidden names, but they work.
Added Test::Exception to the PREREQ_PM (thanks to rjray
for
pointing that out).
Updated the docs and examples.
0.65 Wed May 11, 2005
Added Term::ReadLine support to aiprolog shell.
Added Term::ReadKey support to aiprolog shell.
Minor doc corrections.
0.64 Mon May 09, 2005
Change
default
behavior to AI::Prolog->raw_results(1).
Added quote() method to allow Perl variables to be quoted
and used at Prolog terms.
Added list() method to allow a Perl list to be turned into
a Prolog list.
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
148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
div(X,Y).
mod(X,Y).
ge(X,Y).
gt(X,Y).
le(X,Y).
lt(X,Y).
Added towers of hanoi and a scheduler to the examples/.
0.61 Mon Feb 21, 2005
Added var(X).
Disabled tracing in the aiprolog shell (whoops!)
0.6 Sun Feb 20, 2005
Added tests
for
the cut, number, and clause classes.
Added stub tests
for
step and primitive classes.
Ensured that all classes at least had stub documentation and added
POD tests.
Updated the TODO list in AI::Prolog.
0.5 Sun Feb 13, 2005
Put the database into its own class.
Pulled engine backtracking out into its own method.
Reduced the scope of many of the Engine::_run variables to ensure data
wasn't leaking.
Added simple
"aiprolog"
shell.
Added cut operator, retract(X), and assert(X).
Added more examples and simple adventure game to both demonstrate the shell
and how Prolog works.
Completely reworked the engine internals to allow new predicates to be
easily added.
* XXX Also screwed up the numbering scheme. Grumble XXX *
0.04 Sun Jan 30, 2005
Added support
for
quoting terms.
Added Engine->formatted method which allows results to be returned as
either strings or as data structures. The latter is more useful.
When using AI::Prolog, the results() method now returns a
"results"
object.
modules is now officially discouraged.
Massive documentation update.
0.03 Sun Jan 23, 2005
Added tests
for
built-in predicates.
Added query() function to AI::Logic::Engine to allow successive queries
without a new bootstrap.
0.02 Sun Jan 23, 2005
First public release of AI::Prolog.
0.01 Thu Jan 20 20:47:49 PST 2005
original version; created by make_project 0.1
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657bin/aiprolog
Changes
data/sleepy.pro
data/spider.pro
examples/append.pl
examples/benchmark.pl
examples/cut.pl
examples/data_structures.pl
examples/hanoi.pl
examples/if_else.pl
examples/member.pl
examples/monkey.pl
examples/path.pl
examples/schedule.pl
examples/trace.pl
lib/AI/Prolog.pm
lib/AI/Prolog/Article.pod
lib/AI/Prolog/Builtins.pod
lib/AI/Prolog/ChoicePoint.pm
lib/AI/Prolog/Cookbook.pod
lib/AI/Prolog/Engine.pm
lib/AI/Prolog/Engine/Primitives.pm
lib/AI/Prolog/Introduction.pod
lib/AI/Prolog/KnowledgeBase.pm
lib/AI/Prolog/Parser.pm
lib/AI/Prolog/Parser/PreProcessor.pm
lib/AI/Prolog/Parser/PreProcessor/Math.pm
lib/AI/Prolog/Term.pm
lib/AI/Prolog/Term/Cut.pm
lib/AI/Prolog/Term/Number.pm
lib/AI/Prolog/TermList.pm
lib/AI/Prolog/TermList/Clause.pm
lib/AI/Prolog/TermList/Primitive.pm
lib/AI/Prolog/TermList/Step.pm
Makefile.PL
MANIFEST This list of files
META.yml Module meta-data (added by MakeMaker)
README
t/01pod.t
t/05examples.t
t/10choicepoint.t
t/20term.t
t/25cut.t
t/25number.t
t/30termlist.t
t/35clause.t
t/35primitive.t
t/35step.t
t/40parser.t
t/50engine.t
t/60aiprolog.t
t/70builtins.t
t/80math.t
t/80preprocessor.t
t/80preprocessor_math.t
t/90results.t
t/99regression.t
1234567891011121314---
#YAML:1.0
name: AI-Prolog
version: 0.741
abstract: Perl extension
for
logic programming.
author:
- Curtis
"Ovid"
Poe
license: unknown
distribution_type: module
configure_requires:
ExtUtils::MakeMaker: 0
build_requires:
ExtUtils::MakeMaker: 0
requires:
aliased: 0.11
Makefile.PL view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132333435363738394041424344use
ExtUtils::MakeMaker;
my
(
@program
,
@extra_modules
);
<<"END_NOTE";
The 'aiprolog' shell is optional. If you choose to install it, Term::ReadLine
and Term::ReadKey will be added to your list of prerequisites.
END_NOTE
if
(prompt(
"Do you wish to install the 'aiprolog' shell?"
,
"y"
) =~ /^[Yy]/ )
{
@program
= (
EXE_FILES
=> [
"bin/aiprolog"
] );
@extra_modules
= (
'Term::ReadLine'
=> 1.01,
'Term::ReadKey'
=> 2.21,
);
}
WriteMakefile(
'NAME'
=>
'AI::Prolog'
,
'VERSION_FROM'
=>
'lib/AI/Prolog.pm'
,
@program
,
'PREREQ_PM'
=> {
'aliased'
=> 0.11,
'Clone'
=> 0.15,
'Exporter::Tidy'
=> 0.06,
'Hash::AsObject'
=> 0.05,
'Pod::Usage'
=> 1.12,
'Regexp::Common'
=> 2.119,
'Text::Balanced'
=> 1.95,
'Text::Quote'
=> 0.03,
'Scalar::Util'
=> 0,
'Hash::Util'
=> 0,
@extra_modules
},
( $] >= 5.005
? (
ABSTRACT_FROM
=>
'lib/AI/Prolog.pm'
,
AUTHOR
=>
'Curtis "Ovid" Poe'
)
: ()
),
);
1234567891011AI::Prolog version 0.02
=====================
INSTALLATION
To install this module type the following:
perl Makefile.PL
make
make test
make install
bin/aiprolog view on Meta::CPAN
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354#!/usr/local/bin/perl
eval
'exec /usr/local/bin/perl -S $0 ${1+"$@"}'
if
0;
# not running under some shell
use
warnings;
use
strict;
use
Term::ReadLine;
use
Term::ReadKey;
use
Pod::Usage 1.12;
AI::Prolog::Engine->formatted(1);
# '$Id: aiprolog,v 1.7 2005/08/06 23:28:40 ovid Exp $';
my
$term
= Term::ReadLine->new(
'AI::Prolog'
);
my
$OUT
=
$term
->OUT || \
*STDOUT
;
use
Carp;
$SIG
{__DIE__} = \
&Carp::confess
;
my
$file
=
shift
;
my
$program
=
''
;
if
(
$file
) {
open
FH,
"< $file"
or
die
"Could not open ($file) for reading: $!"
;
$program
=
do
{
local
$/; <FH> };
}
my
$prolog
= Prolog->new(
$program
);
my
$version
= Prolog->VERSION;
$OUT
<<"END_WELCOME";
Welcome to AI::Prolog v $version
Copyright (c) 2005-2006, 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 'help.' for for a list of built-ins or 'help("\$builtin").' for help on a
specific built-in.
END_WELCOME
my
$COMMAND
=
qr/^%\s*/
;
my
$RESULTS
= 0;
my
$MORE
= 1;
while
(
$prolog
->
continue
) {
my
$query
=
$term
->
readline
(
"?- "
);
chomp
$query
;
next
unless
$query
;
$term
->addhistory(
$query
);
$OUT
"\n"
;
if
(
$query
=~ /^\s*\?/ ) {
help();
next
;
}
bin/aiprolog view on Meta::CPAN
596061626364656667686970717273747576777879808182838485868788899091929394
}
elsif
(
$query
=~ /${COMMAND}more/i) {
$MORE
= 1;
}
elsif
(
$query
=~ /${COMMAND}
no
\s
*more
/i) {
$MORE
= 0;
}
next
;
}
eval
{
$prolog
->query(
$query
)};
if
($@) {
warn
$@;
next
;
}
$RESULTS
= 1;
show_results(
$prolog
);
while
(
$MORE
&& user_wants_more()) {
show_results(
$prolog
);
}
}
sub
show_results {
return
unless
$RESULTS
;
my
(
$prolog
) =
@_
;
my
$results
=
$prolog
->results;
$results
||=
''
;
# otherwise it's an arrayref
$OUT
$results
,
" "
;
unless
(
$results
) {
$OUT
"No\n"
;
$RESULTS
= 0;
}
}
sub
user_wants_more {
return
unless
$RESULTS
;
bin/aiprolog view on Meta::CPAN
111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
-verbose
=> 2,
-input
=> \
*DATA
,
-exitval
=>
'NOEXIT'
,
});
}
__DATA__
=head1 NAME
aiprolog -- A simple Prolog shell using AI::Prolog.
=head1 SYNOPSIS
usage: aiprolog <optional prolog program name>
=head1 DESCRIPTION
C<aiprolog> is a simple prolog shell using L<AI::Prolog> as the backend.
See the documentation for more detail on the Prolog features that L<AI::Prolog>
currently accepts.
=head2 Commands
Commands specific to aiprolog shell:
"% more" -- enables prompting for more results (default)
"% no more" -- disables prompting for more results
"% nomore" -- same as "no more"
"% halt" -- stops the shell
"% help" -- display this message
Note that the percent sign must preceed the command. The percent sign
indicates a Prolog comment. Without that, aiprolog will think you're trying to
execute a prolog command.
aiprolog-specific commands are case-insensitive.
=head2 Typical session
Save the following to a file named "append.pro":
append([],X,X).
append([W|X], Y, [W|Z]) :- append(X,Y,Z).
Then load it into the C<aiprolog> shell by typing this at a shell:
aiprolog path/to/append.pro
Alternatively, once in the shell, you can load the program with:
consult('path/to/append.prog').
In the shell, you should be greeted by a query prompt "?-". At this prompt,
you can issue queries against the program. Try entering the following query:
append(X,Y,[1,2,3,4]).
bin/aiprolog view on Meta::CPAN
184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227append([1,2],[3,4],[1,2,3,4]) ;
append([1,2,3],[4],[1,2,3,4]) ;
append([1,2,3,4],[],[1,2,3,4]) ;
No
?-
The
"No"
is just Prolog's way of telling you there are
no
more results which
satisfy your query. If you stop trying to satisfy results
before
all solutions
have been found, you might see something like this:
?- append(X,Y,[1,2,3,4]).
append([],[1,2,3,4],[1,2,3,4]) ;
append([1],[2,3,4],[1,2,3,4]) ;
append([1,2],[3,4],[1,2,3,4])
Yes
?-
The
"Yes"
simply says that Prolog found results
for
you.
=head2 The game
If you are hoping to use this to play the bundled "Spider" game, I recommend
the following:
aiprolog location/of/spider.pro
?- % no more
That disables the pause where the shell waits for you to hit a ';' to get more
results or hit enter to continue. It gets very annoying while playing the
game, though it's useful when you really want to program.
Then issue the "start" command (defined in "spider.pro").
?- start.
data/sleepy.pro view on Meta::CPAN
1234567891011121314/*
"Sleepy"
-- a sample adventure game, by David Matuszek. */
/* In standard Prolog, all predicates are
"dynamic"
: they
can be changed during execution. SWI-Prolog requires such
predicates to be specially marked. */
% :- dynamic at/2, i_am_at/1, i_am_holding/1, alive/1,
% lit/1, visible_object/1.
/* This routine is purely
for
debugging purposes. */
%dump
:- listing(at), listing(i_am_at), listing(i_am_holding),
% listing(alive), listing(lit), listing(visible_object).
data/sleepy.pro view on Meta::CPAN
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
lit(bedroom),
retract(at(fly, den)),
assert(at(fly, bedroom)).
optional_buzz_off :-
buzz_off.
optional_buzz_off.
/* Under UNIX, the
"halt."
command quits Prolog but does not
remove the output window. On a PC, however, the window
disappears
before
the final output can be seen. Hence this
routine requests the user to perform the final
"halt."
*/
finish :-
nl,
(
'The game is over. Please enter the "halt." command.'
),
nl.
/* This rule just prints out game instructions. */
instructions :-
nl,
(
'Enter commands using standard Prolog syntax.'
), nl,
(
'Available commands are:'
), nl,
(
'start. -- to start the game.'
), nl,
(
'n. s. e. w. u. d. -- to go in that direction.'
), nl,
(
'take(Object). -- to pick up an object.'
), nl,
(
'drop(Object). -- to put down an object.'
), nl,
(
'use(Object). -- to manipulate an object.'
), nl,
(
'look. -- to look around you again.'
), nl,
(
'on. off. -- to control the room lights.'
), nl,
(
'sleep. -- to try to go to sleep.'
), nl,
(
'instructions. -- to see this message again.'
), nl,
data/spider.pro view on Meta::CPAN
123456789101112%
"Spider"
-- A Sample Adventure Game in Prolog
% David Matuszek, Villanova University
% This defines
my
current location
i_am_at(meadow).
% These facts describe how the rooms are connected.
path(spider, d, cave).
path(cave, u, spider).
data/spider.pro view on Meta::CPAN
163164165166167168169170171172173174175176177178179180181182183
nl.
% This rule just prints out game instructions.
help :-
instructions.
instructions :-
nl,
(
'Enter commands using standard Prolog syntax.'
), nl,
(
'Available commands are:'
), nl,
(
'start. -- to start the game.'
), nl,
(
'n. s. e. w. u. d. -- to go in that direction.'
), nl,
(
'take(Object). -- to pick up an object.'
), nl,
(
'drop(Object). -- to put down an object.'
), nl,
(
'kill. -- to attack an enemy.'
), nl,
(
'look. -- to look around you again.'
), nl,
(
'instructions. -- to see this message again.'
), nl,
(
'halt. -- to end the game and quit.'
), nl,
nl.
examples/append.pl view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132#!/usr/local/bin/perl
use
strict;
use
warnings;
use
Data::Dumper;
$Data::Dumper::Indent
= 0;
$Data::Dumper::Terse
= 1;
use
AI::Prolog 0.64;
my
$prolog
= AI::Prolog->new(
<<"END_PROLOG");
append([], X, X).
append([W|X], Y, [W|Z]) :- append(X, Y, Z).
END_PROLOG
"Appending two lists 'append([a],[b,c,d],Z).'\n"
;
$prolog
->query(
'append([a],[b,c,d],Z).'
);
while
(
my
$result
=
$prolog
->results) {
Dumper(
$result
),
"\n"
;
}
"\nWhich lists appends to a known list to form another known list?\n'append(X,[b,c,d],[a,b,c,d]).'\n"
;
$prolog
->query(
'append(X,[b,c,d],[a,b,c,d]).'
);
while
(
my
$result
=
$prolog
->results) {
Dumper(
$result
),
"\n"
;
}
"\nWhich lists can be appended to form a given list?\n'append(X, Y, [foo, bar, 7, baz]).'\n"
;
my
$list
=
$prolog
->list(
qw/foo bar 7 baz/
);
$prolog
->query(
"append(X,Y,[$list])."
);
while
(
my
$result
=
$prolog
->results) {
Dumper(
$result
),
"\n"
;
}
examples/benchmark.pl view on Meta::CPAN
1234567891011121314151617181920212223#!/usr/local/bin/perl
use
strict;
use
warnings;
use
Benchmark;
use
AI::Prolog;
my
$prolog
= AI::Prolog->new(benchmark());
my
$t0
= new Benchmark;
for
(1 .. 10) {
$prolog
->query(
'nrev30.'
);
while
(
my
$result
=
$prolog
->results) {
$_
,
' '
,
@$result
,$/;
}
}
my
$t1
= new Benchmark;
my
$td
= timediff(
$t1
,
$t0
);
"the code took:"
,timestr(
$td
),
"\n"
;
sub
benchmark {
return
<<
" END_BENCHMARK"
;
append([],X,X).
examples/cut.pl view on Meta::CPAN
12345678910111213141516171819202122232425262728293031#!/usr/local/bin/perl -l
use
strict;
use
warnings;
my
$prolog
= Prolog->new(
<<'END_PROLOG');
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
Engine->formatted(1);
$prolog
->query(
'append(X,Y,[a,b,c,d]).'
);
"Without a cut:\n"
;
while
(
my
$result
=
$prolog
->results) {
$result
;
}
$prolog
= Prolog->new(
<<'END_PROLOG');
append([], X, X) :- !. % note the cut operator
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
"\nWith a cut:\n"
;
$prolog
->query(
'append(X,Y,[a,b,c,d]).'
);
while
(
my
$result
=
$prolog
->results) {
$result
;
}
examples/data_structures.pl view on Meta::CPAN
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061#!/usr/local/bin/perl -l
use
strict;
use
warnings;
use
Data::Dumper;
$Data::Dumper::Indent
= 0;
use
AI::Prolog;
# note that the following line sets an experimental interface option
AI::Prolog->raw_results(0);
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(LIST1,LIST2,[a,b,c,d]).'
);
while
(
my
$result
=
$logic
->results) {
Dumper(
$result
->LIST1);
Dumper(
$result
->LIST2);
}
AI::Prolog::Engine->raw_results(1);
$logic
->query(
'append([X|Y],Z,[a,b,c,d]).'
);
while
(
my
$result
=
$logic
->results) {
Dumper(
$result
);
}
# [HEAD|TAIL] syntax is buggy in queries with result object
#AI::Prolog::Engine->raw_results(0);
#$logic->query('append([X|Y],Z,[a,b,c,d]).');
#while (my $result = $logic->results) {
# print Dumper($result->X);
# print Dumper($result->Y);
# print Dumper($result->Z);
#}
AI::Prolog::Engine->raw_results(0);
$logic
= AI::Prolog->new(thief_prog());
$logic
->query(
'steals(badguy, GOODS, VICTIM).'
);
while
(
my
$result
=
$logic
->results) {
printf
"badguy steals %s from %s\n"
=>
$result
->GOODS,
$result
->VICTIM;
}
AI::Prolog::Engine->raw_results(1);
$logic
->query(
'steals(badguy, GOODS, VICTIM).'
);
while
(
my
$result
=
$logic
->results) {
Dumper(
$result
);
}
sub
thief_prog {
return
<<
' END_PROG'
;
steals(PERP, STUFF, VICTIM) :-
thief(PERP),
valuable(STUFF),
owns(VICTIM,STUFF),
not(knows(PERP,VICTIM)).
examples/hanoi.pl view on Meta::CPAN
12345678910111213141516171819202122232425262728#!/usr/bin/perl
use
strict;
use
warnings;
my
$prolog
= Prolog->new(
<<'END_PROLOG');
hanoi(N) :-
move(N, left, center, right).
move(0, _, _, _) :- !.
move(N,A,B,C) :-
M is N - 1,
move(M,A,C,B),
inform(A,B),
move(M,C,B,A).
inform(X,Y) :-
print("Move a disc from the "),
print(X),
print(" pole to the "),
print(Y),
println(" pole").
END_PROLOG
$prolog
->
do
(
'hanoi(4)'
);
examples/if_else.pl view on Meta::CPAN
1234567891011121314151617#!/usr/local/bin/perl -l
use
strict;
use
AI::Prolog;
use
Data::Dumper;
$Data::Dumper::Terse
= 1;
my
$prolog
= AI::Prolog->new(
<<'END_PROLOG');
thief(badguy).
steals(PERP, X) :-
if(thief(PERP), eq(X,rubies), eq(X,nothing)).
END_PROLOG
$prolog
->query(
"steals(badguy,X)."
);
Dumper
$prolog
->results;
$prolog
->query(
"steals(ovid, X)."
);
Dumper
$prolog
->results;
examples/member.pl view on Meta::CPAN
123456789101112131415161718192021222324252627282930#!/usr/local/bin/perl -l
use
strict;
use
warnings;
my
$prolog
= Prolog->new(
<<'END_PROLOG');
member(X,[X|Xs]).
member(X,[_|Ys]) :- member(X,Ys).
teacher(Person) :- member(Person, [randal,bob,sally]).
classroom(Room) :- member(Room, [class1,class2,class3]).
classtime(Time) :- member(Time, [morning_day1,morning_day2,noon_day1,noon_day2]).
END_PROLOG
# note: the other stuff in this example is part of a schedule
# demo that I'll be writing after a few more predicates
# are added
AI::Prolog::Engine->formatted(1);
$prolog
->query(
'classroom(X).'
);
while
(
my
$result
=
$prolog
->results) {
$result
;
}
$prolog
->query(
'teacher(sally).'
);
while
(
my
$result
=
$prolog
->results) {
$result
;
}
examples/monkey.pl view on Meta::CPAN
1234567891011121314151617181920#!/usr/local/bin/perl
# This is the classic Monkey/Banana problem
use
strict;
use
warnings;
use
AI::Prolog;
my
$prolog
= AI::Prolog->new(<<
'END_PROLOG'
);
perform(grasp,
state(middle, middle, onbox, hasnot),
state(middle, middle, onbox,
has
)).
perform(climb,
state(MP, BP, onfloor, H),
state(MP, BP, onbox, H)).
perform(
push
(P1,P2),
state(P1, P1, onfloor, H),
examples/monkey.pl view on Meta::CPAN
24252627282930313233343536
state(P1, BP, onfloor, H),
state(P2, BP, onfloor, H)).
getfood(state(_,_,_,
has
)).
getfood(S1) :- perform(Act, S1, S2),
nl,
(
'In '
),
(S1),
(
' try '
),
(Act), nl,
getfood(S2).
END_PROLOG
$prolog
->query(
"getfood(state(atdoor,atwindow,onfloor,hasnot))."
);
$prolog
->results;
# note that everything is done internally.
# there's no need to process the results
examples/path.pl view on Meta::CPAN
1234567891011121314151617181920212223242526272829#!/usr/local/bin/perl -l
use
strict;
use
warnings;
use
AI::Prolog;
use
Benchmark;
use
Data::Dumper;
$Data::Dumper::Indent
= 0;
my
$query
=
shift
|| 2;
my
$prolog
= AI::Prolog->new(path_prog());
$prolog
->query(
'solve( Dest, L).'
)
if
$query
== 1;
$prolog
->query(
'solve( p(8,8), L).'
)
if
$query
== 2;
$prolog
->query(
'solve( p(2,2), L).'
)
if
$query
== 3;
my
$t0
= new Benchmark;
#$prolog->trace(1);
my
$results
=
$prolog
->results;
Dumper(
$results
);
my
$t1
= new Benchmark;
my
$td
= timediff(
$t1
,
$t0
);
"the code took:"
,timestr(
$td
),
"\n"
;
sub
path_prog {
return
<<
' END_PROG'
;
solve(Dest,L) :-
solve(p(1,1), Dest, L).
solve(S, Dest, Sol) :-
examples/schedule.pl view on Meta::CPAN
12345678910111213141516171819202122232425262728#!/usr/bin/perl
# Write a Prolog program to schedule classes for a department of NG University.
# There are 6 class periods, 6-8pm and 8-10pm on Monday, Wednesday, and Friday
# evenings. There are classrooms A, B, and C, and teachers Jim, Sally, Susan,
# and George. There are classes algebra, geometry, calculus, and analysis, each
# of which has to be taught 2 class periods per week. Jim can only come on
# Mondays. Sally and Susan want to work together. George can only teach the
# 6-8pm periods. Just write the program to print all possible schedules that meet
# these constraints; don?t try to solve the scheduling problem.
use
strict;
use
warnings;
my
$prolog
= Prolog->new(<<
'END_PROLOG'
);
member(X,[X|Xs]).
member(X,[_|Ys]) :- member(X,Ys).
scheduler(L) :- makeList(L,4), different(L).
makeList([],0):- !.
makeList([course(Teacher,Time,Room)|Rest], N) :-
teacher(Teacher),
classtime(Time),
classroom(Room),
examples/schedule.pl view on Meta::CPAN
41424344454647484950515253different([course(Teacher,Time,_)|Rest]) :-
member(course(Teacher,Time,_),Rest),
!, fail.
different([course(_,Time,Room)|T]) :-
member(course(_,Time,Room),T), !, fail.
different([_|T]) :- different(T).
END_PROLOG
use
Data::Dumper;
$Data::Dumper::Indent
= 0;
AI::Prolog::Engine->raw_results(1);
$prolog
->query(
'scheduler(X)'
);
Dumper
$prolog
->results;
# there are more results. We only need the one
examples/trace.pl view on Meta::CPAN
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354#!/usr/local/bin/perl -l
use
strict;
use
warnings;
AI::Prolog->raw_results(0);
# experimental
my
$logic
= Prolog->new(thief_prog());
"Without trace ...\n"
;
$logic
->query(
'steals("Bad guy", STUFF, VICTIM)'
);
while
(
my
$results
=
$logic
->results) {
printf
"Bad guy steals %s from %s\n"
,
$results
->STUFF,
$results
->VICTIM;
}
<<"END_MESSAGE";
The following will be a long trace of Prolog tries to satisfy the
above goal.
Hit Enter to begin.
END_MESSAGE
<STDIN>;
$logic
->
do
(
'trace.'
);
$logic
->query(
'steals("Bad guy", STUFF, VICTIM)'
);
while
(
my
$results
=
$logic
->results) {
printf
"Bad guy steals %s from %s\n"
,
$results
->STUFF,
$results
->VICTIM;
}
<<"END_MESSAGE";
And we'll do it one more time, but this time calling notrace before
the query.
Hit Enter to begin.
END_MESSAGE
<STDIN>;
$logic
->
do
(
'notrace.'
);
$logic
->query(
'steals("Bad guy", STUFF, VICTIM)'
);
while
(
my
$results
=
$logic
->results) {
printf
"Bad guy steals %s from %s\n"
,
$results
->STUFF,
$results
->VICTIM;
}
sub
thief_prog {
return
<<
' END_PROG'
;
steals(PERP, STUFF, VICTIM) :-
thief(PERP),
valuable(STUFF),
owns(VICTIM,STUFF),
lib/AI/Prolog.pm view on Meta::CPAN
123456789101112131415161718192021package
AI::Prolog;
$VERSION
=
'0.741'
;
## no critic
use
strict;
use
Text::Quote;
use
Regexp::Common;
# they don't want pretty printed strings if they're using this interface
Engine->formatted(0);
# Until (and unless) we figure out the weird bug that prevents some values
# binding in the external interface, we need to stick with this as the default
Engine->raw_results(1);
lib/AI/Prolog.pm view on Meta::CPAN
9899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
return
1
unless
$self
->{_engine};
# we haven't started yet!
!
$self
->{_engine}->halt;
}
1;
__END__
=head1 NAME
AI::Prolog - Perl extension for logic programming.
=head1 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 $prolog = AI::Prolog->new($database);
my $list = $prolog->list(qw/a b c d/);
$prolog->query("append(X,Y,[$list]).");
while (my $result = $prolog->results) {
print Dumper $result;
}
=head1 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.
=head1 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.
=head1 QUICKSTART
For those who like to just dive right in, this distribution contains a Prolog
shell called C<aiprolog> and two short adventure games, C<spider.pro> and
C<sleepy.pro>. If you have installed the C<aiprolog> shell, you can run
either game with the command:
aiprolog data/spider.pro
aiprolog data/sleepy.pro
When the C<aiprolog> shell starts, you can type C<start.> to see how to play
the game. Typing C<halt.> and hitting return twice will allow you to exit.
See the C<bin/> and C<data/> directories in the distribution.
Additionally, you can read L<AI::Prolog::Article> for a better description of
how to use C<AI::Prolog>. This document is an article originally published in
The Perl Review (L<http://www.theperlreview.com/>) and which they have
graciously allowed me to redistribute.
See also Robert Pratte's perl.com article, "Logic Programming with Perl and
Prolog" (L<http://www.perl.com/pub/a/2005/12/15/perl_prolog.html>) for more
more examples.
=head1 DESCRIPTION
C<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, C<@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 BartE<225>k's "Guide to Prolog
Programming" at L<http://kti.ms.mff.cuni.cz/~bartak/prolog/index.html>.
Amongst other things, his course uses the Java applet that C<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.
=head1 USING AI::Prolog
There are three basic steps to using C<AI::Prolog>.
=over 4
=item Create the Prolog program.
=item Create a query.
=item Run the query.
=back
For quick examples of how that works, see the C<examples/> directory with this
distribution. Feel free to contribute more.
=head2 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.
=head2 Creating a query
To create a query for the database, use C<query>.
$prolog->query("steals(badguy,X).");
=head2 Running a query
Call the C<results> method and inspect the C<results> object:
while (my $result = $prolog->results) {
# $result = [ 'steals', 'badguy', $x ]
print "badguy steals $result->[2]\n";
}
=head1 BUILTINS
See L<AI::Prolog::Builtins|AI::Prolog::Builtins> for the built in predicates.
=head1 CLASS METHODS
=head2 C<new($program)>
This is the constructor. It takes a string representing a Prolog program:
my $prolog = AI::Prolog->new($program_text);
See L<AI::Prolog::Builtins|AI::Prolog::Builtins> and the C<examples/> directory
included with this distribution for more details on the program text.
Returns an C<AI::Prolog> object.
=head2 C<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 C<STDOUT> and allows you to see how the engine
is trying to satify your goals. Naturally, this slows things down quite a bit.
Calling C<trace> without an argument returns the current C<trace> value.
=head2 C<raw_results([$boolean])>
You can get access to the full, raw results by setting C<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 C<raw_results> without an argument returns the current C<raw_results>
value.
This is the default behavior.
=head2 C<quote($string)>.
This method quotes a Perl string to allow C<AI::Prolog> to treat it as a proper
Prolog term (and not worry about it accidentally being treated as a variable if
it begins with an upper-case letter).
my $perl6 = AI::Prolog->quote('Perl 6'); # returns 'Perl 6' (with quotes)
$prolog->query(qq'can_program("ovid",$perl6).');
At the present time, quoted strings may use single or double quotes as strings.
This is somewhat different from standard Prolog which treats a double-quoted
string as a list of characters.
Maybe called on an instance (the behavior is unchanged).
=head2 C<list(@list)>.
Turns a Perl list into a Prolog list and makes it suitable for embedding into
a program. This will quote individual variables, unless it thinks they are
a number. If you wish numbers to be quoted with this method, you will need to
quote them manually.
This method does not add the list brackets.
my $list = AI::Prolog->list(qw/foo Bar 7 baz/);
# returns: 'foo', 'Bar', 7, 'baz'
$prolog->query(qq/append(X,Y,[$list])./);
May be called on an instance (the behavior is unchanged).
=head1 INSTANCE METHODS
=head2 C<do($query_string)>
This method is useful when you wish to combine the C<query()> and C<results()>
methods but don't care about the results returned. Most often used with the
C<assert(X)> and C<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 C<query()> method merely builds the query. Not
until the C<results()> method is called is the command actually executed.
=head2 C<query($query_string)>
After instantiating an C<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 C<$self>.
=head2 C<results>
After a query has been issued, this method will return results satisfying the
query. When no more results are available, this method returns C<undef>.
while (my $result = $prolog->results) {
# [ 'grandfather', $ancestor, 'julie' ]
print "$result->[1] is a grandfather of julie.\n";
}
If C<raw_results> is false, the return value will be a "result" object with
methods corresponding to the variables. This is currently implemented as a
L<Hash::AsObject|Hash::AsObject> so the caveats with that module apply.
Please note that this interface is experimental and may change.
$prolog->query('steals("Bad guy", STUFF, VICTIM)');
while (my $r = $prolog->results) {
print "Bad guy steals %s from %s\n", $r->STUFF, $r->VICTIM;
}
See C<raw_results> for an alternate way of generating output.
=head1 BUGS
See L<AI::Prolog::Builtins|AI::Prolog::Builtins> and
L<AI::Prolog::Engine|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.
=head1 TODO
=over 4
=item * Why does this take so long to run?
perl examples/path.pl 3
lib/AI/Prolog.pm view on Meta::CPAN
402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462=item * Better error reporting.
=back
=head1 EXPORT
None by default. However, for convenience, you can choose ":all" functions to
be exported. That will provide you with C<Term>, C<Parser>, and C<Engine>
classes. This is not recommended and most support and documentation will now
target the C<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";
}
=head1 SEE ALSO
L<AI::Prolog::Introduction>
L<AI::Prolog::Builtins>
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
X-Prolog: L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
Roman BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
This work is based on W-Prolog, L<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 L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
with Dr. Jean Vaucher's permission.
=head1 ACKNOWLEDGEMENTS
Patches and other help has also been provided by: Joshua ben Jore and
Sean O'Rourke.
=head1 COPYRIGHT AND LICENSE
Copyright 2005 by Curtis "Ovid" Poe
lib/AI/Prolog/Article.pod view on Meta::CPAN
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172=head1 Logic Programming in Perl
=head2 Introduction
A programmer who hasn't been exposed to all four of the imperative,
functional, objective, and logical programming styles has one or more
conceptual blindspots. It's like knowing how to boil but not fry.
Programming is not a skill one develops in five easy lessons.
-- Tom Christiansen
By now, many Perl programmers know that the language offers support for
imperative, objective, and functional programming. However, logic programming
seems to be a lost art. This article attempts to shed a little light on the
subject. It's not that Prolog can do things that Perl cannot or vice versa.
Instead, Prolog does some things more naturally than Perl -- and vice versa.
In fact, while I introduce you to Prolog, I won't be teaching a bunch of nifty
tricks to make your Perl more powerful. I can't say what needs you may have
and, in any event, the tools that allow logic programming in Perl are generally
alpha quality, thus making them unsuitable for production environments.
=head2 What is Logic Programming?
Logic programming is somewhat of a mystery to many Perl programmers because,
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.
It's more difficult to compile and get running, but it's faster and much more
powerful. Luke Palmer's new C<Logic> distribution takes a somewhat different
approach to the same problem space.
=head2 3 Things to Learn
Most programming in Prolog boils down to learning three things: facts, rules,
and queries. The basics of these can be learned in just a few minutes and will
let you read many Prolog programs.
=head3 Facts
Facts in Prolog are stored in what is called the I<database> or I<knowledge
base>. This isn't a PostgreSQL or SQLite database, but a plain-text file. In
fact, you would call it a program and I'd be hard-pressed to argue with you, so
I won't. In fact, I'll often refer to these as Prolog "programs" just to avoid
confusion.
Facts look like this:
gives(tom, book, sally).
B<Note:> While the order of the arguments is technically not relevant, there
is a convention to more or less read the arguments left to right. The above
fact can be read as "tom gives the book to sally." Of course, it is sometimes
read as "sally gives the book to tom", but whichever order you adopt, you must
lib/AI/Prolog/Article.pod view on Meta::CPAN
8081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131followed by a slash and the arity (
"gives/3"
in this example) is called the
I<predicate>. A predicate can have as many clauses as you like.
gives(tom, book, sally).
gives(tom, book, martin).
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
a particular built-in predicate works.
And that pretty much covers most of what you need to know about facts.
=head3 Rules
Rules, like facts, are stored in the program. Rules describe how we can infer
new facts, even though we haven't explicitly stated them.
gives(tom, book, SOMEONE) :-
person(SOMEONE),
likes(tom, SOMEONE).
This rule states that "Tom will give a book to anyone who Tom likes." Note that
we are not telling Prolog how to figure out to whom Tom will give books.
Instead, we have merely defined the conditions under which Tom is willing to
part with his material possessions.
To understand rules, read the neck operator, C<:->, as "if" and commas outside
of argument lists as "and." Further, arguments beginning with upper-case
letters are I<variables>, such as C<SOMEONE> in the rule above. Note that only
the first letter needs to be capitalized; C<Someone> would also be a variable,
as would C<SomeOne> or C<SOmeoNe>.
Of course, we could simply enumerate the relationships:
lib/AI/Prolog/Article.pod view on Meta::CPAN
136137138139140141142143144145146147148149150151152153154155156157158gives(tom, book, charlie).
gives(tom, book, ovid).
However, this quickly become unweildy as the number of people in
our
program
grows.
=head3 Queries
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.
lib/AI/Prolog/Article.pod view on Meta::CPAN
160161162163164165166167168169170171172173174175176177178179180181182183184185186?- 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.
Let's take a closer look at this, first focusing on the C<aiprolog> shell.
Assuming you've installed C<AI::Prolog> and said
"yes"
to installing the
C<aiprolog> shell, enter the following text in a file and save it as
I<gives.pro>. Note that lines beginning
with
a percent sign (C<%>) are
single-line comments.
% who are people?
person(bob).
person(sally).
person(tom).
person(alice).
% who likes whom?
lib/AI/Prolog/Article.pod view on Meta::CPAN
197198199200201202203204205206207208209210211212213214215216217218219220221222223224225gives(WHO, WHAT, WHOM) :-
has
(WHO, WHAT),
person(WHOM),
likes(WHO, WHOM).
gives(tom,book,harry).
When starting the shell, you can
read
in a file by supplying as a name on the
command line:
$ aiprolog gives.pro
Alternately, you can I<consult> the file from the shell:
$ aiprolog
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
lib/AI/Prolog/Article.pod view on Meta::CPAN
227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292Now that you've loaded the program into the shell, issue the following query:
?- gives(X,Y,Z).
The shell should respond:
gives(tom, book, bob)
It will appear to hang. It wants to know
if
you wish
for
more results or
if
you are going to
continue
. Typing a semicolon (;) tells Prolog that you want
it to I<resatisfy> the goal. In other words, you're asking Prolog
if
there are
more solutions. If you keep hitting the semicolon, you should see something
similar to the following:
?- gives(X,Y,Z).
gives(tom, book, bob) ;
gives(tom, book, alice) ;
gives(alice, ring, bob) ;
gives(tom, book, harry) ;
No
?-
That final
"No"
is Prolog telling you that there are
no
more results which
satisfy your goal (query). (If you hit I<Enter>
before
Prolog prints
"No"
, it
will
"Yes"
, letting you know that it found results
for
you. This is
standard behavior in Prolog.)
One thing you might notice is that the
last
result, C<gives(tom, book, harry)>,
does not match the rule we set up
for
C<gives/3>. However, we get this result
because we chose to hard-code this fact as the
last
line of the Prolog program.
=head2 How this works
At this point, it's worth having a bit of a digression to explain how this
works.
Many deductive systems in artificial intelligence are based on two algorithms:
backtracking and unification. You're probably already familiar with backtracking
from regular expressions. In fact, regular expressions are very similar to
Prolog in that you specify a pattern for your data and let the regex engine
worry about how to do the matching.
In a regular expression, if a partial match is made, the regex engine remembers
where the end of that match occurred and tries to match more of the string. If
it fails, it backtracks to the last place a successful match was made and sees
if there are alternative matches it can try. If that fails, it keeps
backtracking to the last successful match and repeats that process until it
either finds a match or fails completely.
Unification, described in a fit of wild hand-waving, attempts to take two
logical terms and "unify" them. Imagine you have the following two lists:
( 1, 2, undef, undef, 5 )
( 1, 2, 3, 4, undef )
Imagine that undef means "unknown". We can unify those two lists because every
element that is known corresponds in the two lists. This leaves us with a list
of the integers one through five.
( 1, 2, 3, 4, 5 )
lib/AI/Prolog/Article.pod view on Meta::CPAN
307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438( 1, 23,
undef
,
undef
,
undef
)
( 1, 2, 3, 4,
undef
)
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).
=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).
lib/AI/Prolog/Article.pod view on Meta::CPAN
441442443444445446447448449450451452453454455456457458459460461462463464465466467male(tim).
father(Person) :-
parent(Person, _),
male(Person).
If you run this program, it will quite happily
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)
lib/AI/Prolog/Article.pod view on Meta::CPAN
478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
<<== Backtrack:
= Goals:
male(sally)
==> Try: male(tim) :- null
<<== Backtrack:
[etc.]
Now
if
you really want to have fun
with
it, notice how you can rearrange the
clauses in the program at will and Prolog will
return
the same results (though
the order will likely change). This is because
when
one programs in a purely
declarative style, the order of the statements
no
longer matters. Subtle bugs
caused by switching two lines of code usually go away.
=head2 Prolog versus Perl
Now that you have a beginning understanding of what Prolog can do and how it
works internally, let's take a look at some of the implications of this. By
now, you know that the following can be read as "Ovid loves Perl":
loves(ovid, perl).
Assuming you've consulted a file with that fact in it, querying to find out
what I love is simple:
?- loves(ovid, WHAT).
In Perl, it's also pretty simple:
%loves = ( ovid => 'perl' );
$what = $loves{ovid};
But how do we find out who loves Perl? In Prolog:
loves(WHO, perl).
In Perl, however, we have two options, neither of them particularly good. We
can scan the C<%loves> hash for entries whose value is Perl, but this is
C<O(n)> when we want to stick with our simple C<O(1)> check. Thus, a more
common solution is to reverse the hash:
%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).
lib/AI/Prolog/Article.pod view on Meta::CPAN
537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613If we simply
reverse
the hash
for
those entries, we lose three of those names.
So we have to play
with
this some more.
while
(
my
(
$person
,
$thing
) =
each
%loves
) {
push
@{
$who_loves
{
$thing
} },
$person
;
}
Oh,
wait
. This fails too. You see, I'm fickle.
loves(ovid, perl).
loves(ovid, prolog).
loves(ovid,
'Perl 6'
).
(The quotes
around
"Perl 6"
tell
Prolog that this is a single value and that
it's a constant, not a variable.)
How
do
I find out everything Ovid loves? In Prolog, the query doesn't change:
loves(ovid, WHAT).
In Perl,
our
original hash wasn't enough.
my
%loves
= (
ovid
=> [
qw/perl prolog Perl6/
],
alice
=> [
'perl'
],
bob
=> [
'perl'
],
charlie
=> [
'perl'
],
);
my
%who_loves
;
while
(
my
(
$person
,
$things
) =
each
%loves
) {
foreach
my
$thing
(
@$things
) {
push
@{
$who_loves
{
$thing
} },
$person
;
}
}
Now that's starting to get really ugly. To represent and search that data in
Prolog is trivial. Now
do
you really want to have fun?
gives(tom, book, sally).
How would you represent that in Perl? There could be multiple gift-givers,
each
gift-giver could give multiple things. There can be multiple recipients.
Representing this cleanly in Perl would not be easy. In fact,
when
handling
relational data, Perl
has
several weaknesses in relation to Prolog.
=over 4
=item * Data must often be duplicated to handle bi-directional relations.
=item * You must remember to synchronize data structures if the data changes.
=item * Often you need to change your code if your relations change.
=item * The code can get complex, leading to more bugs.
=back
=head2 Prolog versus SQL
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');
lib/AI/Prolog/Article.pod view on Meta::CPAN
616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671sqlite> 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
if
you had to
write
all of your programs in SQL. Could you
do
it? You could
with
Prolog. Further, there's one huge reason why one might choose Prolog:
recursion. Some SQL implementations have non-standard support
for
recursion,
but usually recursive procedures are simulated by multiple calls from the
programming language using SQL.
Let's take a look at recursion and see why this is a win.
=head2 Recursion and Lists
In Prolog, lists are not data structures. They're actually implemented in
terms of something referred to as the "dot functor" (./2). For our purposes,
we'll ignore this and pretend they're really data types. In fact, there's
going to be a lot of handwaving here so forgive me if you already know Prolog.
If you know LISP or Scheme, the following will be very familiar.
A list in Prolog is usually represented by a series of terms enclosed in square
brackets:
owns(alice, [bookcase, cats, dogs]).
A list consists of a head and a tail. The tail, in turn, is another list with
a head and tail, continuing recursively until the tail is an empty list. This
can be represented as follows:
[ HEAD | TAIL ]
lib/AI/Prolog/Article.pod view on Meta::CPAN
679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727member(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?
has
(PERSON, THING) :-
owns(PERSON, STUFF),
member(THING, STUFF).
And the query C<
has
(alice, cats)> will now succeed. However, as mentioned
previously, Prolog predicates can often be reused to deduce information that
you and I could deduce. Thus, we can find out who owns cats (C<
has
(WHO,
cats)>), everything Alice
has
(C<
has
(alice, WHAT)>), or everything that
everyone
has
(C<
has
(WHO, WHAT>).
Why does that work? Well, going back to the C<member/2> predicate, we can find
out
if
a term is an element of a list:
member(ovid, SOMELIST).
Or we can find all members of a list:
member(X, SOMELIST). % assumes SOMELIST is bound to a list
Now that might seem kind of nifty, but appending lists in Prolog is truly
sublime. Many consider understanding the power of the C<append/3> predicate
the true gateway to appreciating logic programming. So, without further ado:
append([], X, X).
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
lib/AI/Prolog/Article.pod view on Meta::CPAN
729730731732733734735736737738739740741742743744745746747748749It 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,
one would see in regular Prolog systems. It's been reproduced here
for
clarity.)
?- append([1,2,3], [4,5], Z).
Z = [1,2,3,4,5]
figure out which list to append to C<X> to create C<Z>.
?- append([1,2,3], Y, [1,2,3,4,5]).
lib/AI/Prolog/Article.pod view on Meta::CPAN
757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
X = [], Y = [1,2,3,4,5]
X = [1], Y = [1,2,3,4]
X = [1,2], Y = [1,2,3]
X = [1,2,3], Y = [1,2]
X = [1,2,3,4], Y = [1]
X = [1,2,3,4,5], Y = []
And the Perl equivalent:
my
$prolog
= AI::Prolog->new(<<
"END_PROLOG"
);
append([], X, X).
append([W|X], Y, [W|Z]) :- append(X, Y, Z).
END_PROLOG
my
$list
=
$prolog
->list(
qw/1 2 3 4 5/
);
$prolog
->query(
"append(X,Y,[$list])."
);
while
(
my
$results
=
$prolog
->results ) {
my
(
$x
,
$y
,
$z
) = @{
$results
}[ 1, 2, 3 ];
$" =
', '
;
# Array separator
"[@$x], [@$y], [@$z]\n"
;
}
As you can see, Prolog lists will be returned to Perl as array references.
C<<
$results
->[0] >> is the name of the predicate, C<append>, and the
next
three
elements are the successive
values
generated by Prolog.
=head1 Problems with Prolog
This article wouldn't be complete without listing some of the issues that have
hampered Prolog.
Perhaps the most significant is the depth-first exhaustive search algorithm
used for deduction. This is slow and due to the dynamically typed nature of
Prolog, many of the optimizations that have been applied to databases cannot be
applied to Prolog. Many Prolog programmers, understanding how the language
works internally, use the "cut" operator, C<!> (an exclamation point), to prune
the search trees. This leads to our next problem.
The "cut" operator is what is known as an "extra-logical" predicate. This,
along with I/O and predicates that assert and retract facts in the database
are a subject of much controversy. They cause issues because they frequently
cannot be backtracked over and the order of the clauses sometimes becomes
important when using them. They can be very useful and simplify many problems,
but they are more imperative in nature than logical and can introduce subtle
bugs.
Math is also handled poorly in Prolog. Consider the following program:
convert(Celsius, Fahrenheit) :-
Celsius is (Fahrenheit - 32) * 5 / 9.
You can then issue the query C<convert(Celsuis, 32)> and it will dutifully
report that the celsius value is zero. However, C<convert(0, 32)> fails. This
is because Prolog is not able to solve the right hand side of the equation
unless C<Fahrenheit> has a value at the time that Prolog starts examining the
equation. One simplistic way of getting around this limitation is with multiple
predicates and testing which argument is an unbound variable:
convert(Celsius, Fahrenheit) :-
var(Celsius),
not(var(Fahrenheit)),
Celsius is (Fahrenheit - 32) * 5 / 9.
convert(Celsius, Fahrenheit) :-
var(Fahrenheit),
not(var(Celsius)),
Fahrenheit is (Celsius * (9/5)) + 32.
This has a variety of problems, not the least of which is that it offers no
advantages over imperative programming.
ISO-Prolog does not define it, but many Prolog implementations support
constraints and these, though beyond the scope of this article, can get around
this and other issues. They can allow "logical" math and ensure that
"impossible" search branches are never explored. This can help to alleviate
many of the aforementioned concerns.
=head1 Conclusion
We've barely scratched the surface of what the Prolog language can do. The
language has many detractors and some criticisms are quite valid. However, the
language's simplicity and ease-of-use has kept it around. It's an excellent
starting point for understanding logic programming and exploration of AI. In
fact, many common uses for Prolog are heavily used in the AI arena:
=over 4
=item * Agent-based programming
=item * Expert Systems
=item * Fraud prevention (via inductive logic programming)
=item * Natural language processing
=item * Rules-based systems
=item * Scheduling systems
=item * And much more (it was even embedded in Windows NT)
=back
At the present time, I would not recommend C<AI::Prolog> for production work.
Attempts to bring logic programming to Perl are still relatively recent and no
module (to this author's knowledge) is currently ready for widespread use. The
C<AI::Prolog> distribution has a number of sample programs in the C<examples/>
directory and two complete, albeit small, games in the C<data/> directory.
=head1 References
=head2 Online Resources
=over 4
=item Adventure in Prolog
I highly recommend Amzi! Prolog's free online book "Adventure in Prolog." It's
clearly written and by the time you're done, you've built Prolog programs for
an adventure game, a genealogical database, a customer order inventory
application and a simple expert system.
=item Building Expert Systems in Prolog
This free online book, also by Amzi!, walks you through building expert
systems. It includes expert systems for solving Rubik's cube, tax preparation,
car diagnostics and many other examples.
=item Databases Vs AI
This Powerpoint presentation discusses AI in Prolog and compares it to SQL
databases.
=item W-Prolog
Dr. Michael Winikoff kindly gave me permission to port this Java application to
Perl. This formed the basis of the earliest versions.
=item X-Prolog
Jean Vaucher, Ph.D., allowed me to borrow from X-Prolog for the first
implementation of math in C<AI::Prolog>. Currently, C<AI::Prolog> is a hybrid
of these two applications, but it has evolved in a much different direction.
=back
=head2 Books
=over 4
=item Programming in Prolog
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
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/Builtins.pod view on Meta::CPAN
1234567891011121314151617181920212223242526=head1 NAME
AI::Prolog::Builtins - Builtin predicates that AI::Prolog supports
=head1 REVISION
$Id: Builtins.pod,v 1.9 2005/08/06 23:28:40 ovid Exp $
=head2 Comments
Comments begin with a C<%> and terminate at the end of the line or begin with
C</*> and terminate with C<*/>.
=head2 Variables
As in Prolog, all variables begin with an upper-case letter and are not quoted.
In the following example, C<STUFF> is a variable.
steals(badguy, STUFF, "Some rich person").
=head2 Constants
Constants begin with lower-case letters. If you need a constant that begins
with an upper-case letter or contains spaces or other non-alphanumeric
characters, enclose the constant in single or double quotes The quotes will
not be included in the constant.
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
3839404142434445464748495051525354555657Use this instead:
p(X) :- call(X).
=head1 BUILTINS
=over 4
=item !/0
The "cut" operator. This is used when you wish to tell Prolog that you only
need to satisfy a goal once. For example, if you wish to deny someone the
right to rent videos if they have overdue videos, you might use the cut
operator as soon as you see they have any overdue video. The fact that they
have more than one overdue video doesn't matter.
See the C<cut.pl> program in the C<examples/> directory that comes with this
distribution.
=item assert/1
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
606162636465666768697071727374757677787980change in the future. See C<retract(X)>.
assert(loves(ovid,perl)).
=item call/1
Invokes C<X> as a goal.
=item consult/1
Supplied the name of a file containing Prolog code, this will consult the
Prolog code in the file and add its contents to the current knowledgebase.
Will warn if the file cannot be opened.
=item div/2
Succeeds if both terms are bound. The value of the term is X / Y.
Use with C<is(X,Y)>.
is(X, div(N,3)).
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
110111112113114115116117118119120121122123124125126127128129130=item gt/2
Succeeds if both terms are bound and X > Y.
This is the internal form of the infix operator:
X > Y.
=item halt/1
In the C<aiprolog> shell, exist shell. Currently has no other effect.
=item if/3
If C<X> succeeds as a goal, try C<Y> as a goal. Otherwise, try C<Z>.
thief(badguy).
steals(PERP, X) :-
if(thief(PERP), eq(X,rubies), eq(X,nothing)).
=item is/2
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
147148149150151152153154155156157158159160161162163164165166
X <= Y.
=item listing/0
Dumps a listing of all user-defined predicates and how they are defined.
=item listing/1
Dumps a listing of the requested predicate. C<X> must a variable or string
instantiated in I<functor/arity>) form. Note that, unlike most Prolog's, this
means that the followig will not work:
listing(foo/2).
Use this instead:
listing('foo/2').
=item lt/2
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
216217218219220221222223224225226227228229230231232233234235236Prints a newline.
=item not/1
Succeeds if C<X> cannot be proven. This is not negation as we're used to
seeing it in procedural languages.
=item notrace/0
Turns off tracing of Prolog's attempt to satisfy goals.
=item once/1
Stop solving for C<X> if C<X> succeeds. Defined as:
once(X) :- X, !;
=item or/2
Succeeds as a goal if either C<X> or C<Y> succeeds.
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
263264265266267268269270271272273274275276277278279280281282283=item retract/1
Remove facts from the database. You cannot remove rules. This may change in
the future. See C<assert(X)>.
retract(loves(ovid,java)).
=item trace/0
Turns on tracing of Prolog's attempt to satisfy goals.
=item true/0
True goal. Automatically succeeds.
=item var/1
Succeeds if X is an unbound variable. Otherwise, this goal fails.
=item write/1
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351into them :)
=item More builtins.
Currently, we only have a tiny subset of builtins available. More are coming.
=back
=head1 MATH
Since version .70, math is fully available in C<AI::Prolog>. Note that math is
implemented via the
L<AI::Prolog::Parser::PreProcessor::Math|AI::Prolog::Parser::PreProcessor::Math>
module. This module rewrites Prolog math to an internal, predicate-based form
with the L<AI::Prolog::Parser|AI::Prolog::Parser> can parse. This may cause
confusion when debugging.
X is 5 + 7.
% internally converted to
% is(X, plus(5, 7)).
The math predicates are officially deprecated and I<cannot> be used in the same
expression with regular Prolog math.
Number may be integers, floats, doubles, etc. A number that starts with a
minus sign (-) is considered negative. No number may end in a decimal point as
the period is interpreted as the end of a clause. The following is therefore a
syntax error:
X is 5. + 7.
Unfortunately, the parser doesn't yet yell about that. We'll try and figure
out why later.
lib/AI/Prolog/Builtins.pod view on Meta::CPAN
381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433The 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
X \= Y. % Not equal. See caveats
for
ne/2
% etc.
=head1 BUGS
None known.
=head1 SEE ALSO
L<AI::Prolog::Introduction>
L<AI::Prolog>
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
X-Prolog: L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
Roman BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
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.
=head1 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.
lib/AI/Prolog/ChoicePoint.pm view on Meta::CPAN
1234567891011package
AI::Prolog::ChoicePoint;
$REVISION
=
'$Id: ChoicePoint.pm,v 1.5 2005/02/20 18:27:55 ovid Exp $'
;
$VERSION
=
'0.02'
;
use
strict;
use
warnings;
sub
new {
my
(
$class
,
$goal
,
$clause
) =
@_
;
my
$self
=
bless
{
lib/AI/Prolog/ChoicePoint.pm view on Meta::CPAN
2324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
my
$self
=
shift
;
return
" ||"
.
$self
->clause->to_string .
"|| "
;
}
1;
__END__
=head1 NAME
AI::Prolog::ChoicePoint - Create a choicepoint object for the Engine.
=head1 SYNOPSIS
No user serviceable parts inside. You should never be seeing this. This
little snippet is merely used when backtracking and needing to try other
alternatives.
=head1 DESCRIPTION
See L<AI::Prolog|AI::Prolog> for more information. If you must know more,
there are plenty of comments sprinkled through the code.
=head1 SEE ALSO
L<AI::Prolog>
L<AI::Prolog::Introduction>
L<AI::Prolog::Builtins>
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
X-Prolog: L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
Roman BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
=head1 COPYRIGHT AND LICENSE
Copyright 2005 by Curtis "Ovid" Poe
lib/AI/Prolog/Cookbook.pod view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132333435363738394041424344=head1 NAME
AI::Prolog::Cookbook - Recipes for common Prolog problems
=head1 REVISION
$Id: Cookbook.pod,v 1.1 2005/08/06 23:28:40 ovid Exp $
=head1 DESCRIPTION
Logic programming can take some time to get used to. This document is intended
to provide solutions to common problems encountered in logic programming. Many
of the predicates listed here will depend on other predicates defined here. If
in doubt, see L<AI::Prolog::Builtins|AI::Prolog::Builtins> for which predicates
L<AI::Prolog|AI::Prolog> supports directly.
Like most predicates in Prolog, the following predicates can be reused in ways
to generate answers that a human could logically infer from the data presented.
However, many times those "answers" can result in infinite loops. For example,
in the C<gather/3> predicate listed below, we can gather the items from a list
which match the supplied list of indices.
gather([1,3], [a,b,c,d], Result). % Result is [a,c]
Or we can figure out which indices in a list match the resulting values:
gather(Indices, [a,b,c,d], [a,d]). % Indices is [1,4]
However, if we wish to understand which lists will have the given lists for the
given indices, we have an infinite result set. L<AI::Prolog|AI::Prolog> and
(other Prolog implementations) will return one result and then enter an
infinite loop if you request the goal be resatisfied (i.e., if you ask for
another result). If you see behavior such as this in your programs, you can
issue the C<trace.> command to see how Prolog is internally attempting to
satisfy your goal. C<notrace.> will turn off tracing.
=head1 THE PROBLEMS
=head2 Append two lists.
Usage: C<append(List1, List2, Result).>
append([], X, X). % appending an empty list to X yields X
append([W|X], Y, [W|Z]) :-
lib/AI/Prolog/Cookbook.pod view on Meta::CPAN
54555657585960616263646566676869707172737475=head2 Pick a list member by index.
Usage: C<member(Index, Item, List).>
member(1, SearchFor, [SearchFor|_]).
member(Index, SearchFor, [_|Tail]) :-
member(Previous, SearchFor, Tail),
Index is Previous + 1.
Please note that assignment in Prolog is via the C<is/2> infix operator. The
above code will fail if you use C<=/2>. This is a common source of bugs for
programmers new to Prolog. The C<=/2> predicate will unify the right hand side
with the left hand side. It will I<not> evaluate the left hand side. Thus:
X = 3 + Y.
% X is now plus(3,Y) (the internal form of the +/2 infix operator.)
If you prefer your list indices start with zero, alter the first clause as
follows:
member(0, SearchFor, [SearchFor|_]).
lib/AI/Prolog/Cookbook.pod view on Meta::CPAN
90919293949596979899100101102103104105106107108109110gather([2,4], [a,b,c,d,e], Result). % Result = [b,d]
gather(FromIndices, [a,b,c,d,e], [b,d]). % FromIndices = [2,4]
This example is tricky
when
one realizes that one can reuse predicates. In
this case, it might be tempting to see I<which> lists from which one can
gather certain
values
from certain indices. The first
time
you
try
it, you
may get results as follows:
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.
lib/AI/Prolog/Cookbook.pod view on Meta::CPAN
139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
sub
reverse
{
my
@list
=
@_
;
my
@reverse
;
while
(
my
$element
=
shift
@list
) {
unshift
@reverse
,
$element
;
}
return
@reverse
;
}
This method of reversing a list runs in C<O(n)>
time
. However, new Prolog
programmers often
write
what is known as the
"naive reverse"
which uses the
C<append/3> predicate to
reverse
a list:
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.
subset([Head|Tail], List) :-
member(Head, List),
subset(Tail, List).
lib/AI/Prolog/Cookbook.pod view on Meta::CPAN
213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244sort
([Head|Tail], Sorted) :-
partition(Head, Tail, LHS, RHS),
sort
(LHS, Temp1),
sort
(RHS, Temp2),
append(Temp1, [Head|Temp2], Sorted).
Note that (currently), this will only
sort
numeric
values
.
=head1 SEE ALSO
L<AI::Prolog>
L<AI::Prolog::Builtins>
L<AI::Prolog::Introduction>
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
X-Prolog: L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
Roman BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
=head1 COPYRIGHT AND LICENSE
Copyright 2005 by Curtis "Ovid" Poe
lib/AI/Prolog/Engine.pm view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132333435package
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
aliased
'https://metacpan.org/pod/AI::Prolog::TermList::Primitive">AI::Prolog::TermList::Primitive'
;
# The engine is what executes prolog queries.
# Author emeritus: Dr. Michael Winikoff
# Translation to Perl: Curtis "Ovid" Poe
# $prog An initial program - this will be extended
# $term The query to be executed
# This governs whether tracing is done
sub
trace {
my
$self
=
shift
;
if
(
@_
) {
lib/AI/Prolog/Engine.pm view on Meta::CPAN
919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
# The stack holds choicepoints and a list of variables
# which need to be un-bound upon backtracking.
_stack
=> [],
_db
=> KnowledgeBase->new,
_goal
=> TermList->new(
$term
,
undef
),
# TermList
_call
=>
$term
,
# Term
_run_called
=>
undef
,
_cp
=>
undef
,
_retract_clause
=>
undef
,
_trace
=> 0,
# whether or not tracing is done
_halt
=> 0,
# will stop the aiprolog shell
_perlpackage
=>
undef
,
_step_flag
=>
undef
,
} =>
$class
;
lock_keys
%$self
;
# to add a new primitive, use the binding operator (:=) to assign a unique
# index to the primitive and add the corresponding definition to
# @PRIMITIVES.
eval
{
$self
->_adding_builtins(1);
$self
->{_db} = Parser->consult( <<
' END_PROG'
,
$prog
);
ne(X, Y) :- not(eq(X,Y)).
if
(X,Y,Z) :- once(wprologtest(X,R)) , wprologcase(R,Y,Z).
wprologtest(X,yes) :- call(X). wprologtest(X,
no
).
wprologcase(yes,X,Y) :- call(X).
wprologcase(
no
,X,Y) :- call(Y).
not(X) :-
if
(X,fail,true).
or(X,Y) :- call(X).
or(X,Y) :- call(Y).
true.
% := operator. Eventually, I'll make this a fatal error.
% See AI::Prolog::Engine::Builtins to see the code
for
these
! := 1.
call(X) := 2.
fail := 3.
consult(X) := 4.
assert(X) := 5.
retract(X) := 7.
retract(X) :- retract(X).
listing := 8.
listing(X) := 9.
(X) := 10.
lib/AI/Prolog/Engine.pm view on Meta::CPAN
332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439}
sub
_warn {
# convenient testing hook
warn
@_
;
}
sub
do_primitive {
# returns false if fails
my
(
$self
,
$term
,
$c
) =
@_
;
my
$primitive
= AI::Prolog::Engine::Primitives->find(
$c
->ID )
or
die
sprintf
"Cannot find primitive for %s (ID: %d)\n"
,
$term
->to_string,
$c
->ID;
return
unless
my
$result
=
$primitive
->(
$self
,
$term
,
$c
);
return
1
if
RETURN ==
$result
;
$self
->{_goal} =
$self
->{_goal}->
next
;
if
(
$self
->{_goal} ) {
$self
->{_goal}->resolve(
$self
->{_db} );
}
return
1;
}
1;
__END__
=head1 NAME
AI::Prolog::Engine - Run queries against a Prolog database.
=head1 SYNOPSIS
my $engine = AI::Prolog::Engine->new($query, $database).
while (my $results = $engine->results) {
print "$result\n";
}
=head1 DESCRIPTION
C<AI::Prolog::Engine> is a Prolog engine implemented in Perl.
The C<new()> function actually bootstraps some Prolog code onto your program to
give you access to the built in predicates listed in the
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, $/;
}
The need to have a query at the same time you're instantiating the engine is a
bit of a drawback based upon the original W-Prolog work. I will likely remove
this drawback in the future.
=head2 C<formatted([$boolean])>
The default value of C<formatted> is true. This method, if passed a true
value, will cause C<results> to return a nicely formatted string representing
the output of the program. This string will loosely correspond with the
expected output of a Prolog program.
If false, all calls to C<result> will return Perl data structures instead of
nicely formatted output.
If called with no arguments, this method returns the current C<formatted>
value.
Engine->formatted(1); # turn on formatting
Engine->formatted(0); # turn off formatting (default)
if (Engine->formatted) {
# test if formatting is enabled
}
B<Note>: if you choose to use the L<AI::Prolog|AI::Prolog> interface instead of
interacting directly with this class, that interface will set C<formatted> to
false. You will have to set it back in your code if you do not wish this
behavior:
use AI::Prolog;
my $logic = AI::Prolog->new($prog_text);
$logic->query($query_text);
AI::Logic::Engine->formatted(1); # if you want formatted to true
while (my $results = $logic->results) {
print "$results\n";
}
=head2 C<raw_results([$boolean])>
The default value of C<raw_results> is false. Setting this property to a true
value automatically sets C<formatted> to false. C<results> will return the raw
data structures generated by questions when this property is true.
Engine->raw_results(1); # turn on raw results
lib/AI/Prolog/Engine.pm view on Meta::CPAN
459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501time
. It will
return
false
when
there are
no
more results. If C<formatted> is
true, it will
return
a string representation of those results:
while
(
my
$results
=
$engine
->results) {
"$results\n"
;
}
If C<formatted> is false, C<
$results
> will be an object
with
methods matching
the variables in the query. Call those methods to access the variables:
AI::Prolog::Engine->formatted(0);
$engine
->query(
'steals(badguy, STUFF, VICTIM).'
);
while
(
my
$r
=
$engine
->results) {
printf
"badguy steals %s from %s\n"
,
$r
->STUFF,
$r
->VICTIM;
}
If necessary, you can get access to the full, raw results by setting
C<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::Engine->raw_results(1);
$engine
->query(
'steals(badguy, STUFF, VICTIM).'
);
while
(
my
$r
=
$engine
->results) {
# do stuff with $r in the form:
# ['steals', 'badguy', $STUFF, $VICTIM]
}
=head2 C<query($query)>
If you already have an engine object instantiated, call the C<query()> method
for subsequent queries. Internally, when calling C<new()>, the engine
bootstraps a set of Prolog predicates to provide the built ins. However, this
process is slow. Subsequent queries to the same engine with the C<query()>
method can double the speed of your program.
my $engine = Engine->new($query, $database);
while (my $results = $engine->results) {
print $results, $/;
}
$query = Term->new("steals(ovid, X).");
$engine->query($query);
while (my $results = $engine->results) {
lib/AI/Prolog/Engine.pm view on Meta::CPAN
505506507508509510511512513514515516517518519520521522523524=head1 BUGS
None known.
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
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.
=head1 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.
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
123456789101112131415161718192021222324252627282930313233343536## 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;
my
%DESCRIPTION_FOR
;
my
$LONGEST_PREDICATE
=
''
;
sub
_load_builtins {
return
if
keys
%DESCRIPTION_FOR
;
my
$perldoc
= Pod::Perldoc->new;
my
$builtin_pod
=
'AI::Prolog::Builtins'
;
my
(
$found
) =
$perldoc
->grand_search_init( [
$builtin_pod
] )
or
die
"Help failed. Cannot find documentation for $builtin_pod: $!"
;
open
my
$fh
,
'<'
,
$found
or
die
"Cannot open $found for reading: ($!)"
;
my
@lines
= <
$fh
>;
close
$fh
or
die
"Cannot close $found: ($!)"
;
while
(
@lines
) {
my
$line
=
shift
@lines
;
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
139140141142143144145146147148149150151152153154155156157158159160161162163164};
$PRIMITIVES
[4] =
sub
{
# consult/1
my
(
$self
,
$term
,
$c
) =
@_
;
my
$file
=
$term
->getarg(0)->getfunctor;
if
(
open
my
$fh
,
'<'
,
$file
) {
# Avoid do { local $/; <$fh> }. This triggers a bug where
# *two* copies of the string are made. Double space is
# required.
my
$prolog
;
{
local
$/;
$prolog
= <
$fh
>;
}
$self
->{_db}->consult(
$prolog
);
return
CONTINUE;
}
else
{
warn
"Could not open ($file) for reading: $!"
;
return
FAIL;
}
};
$PRIMITIVES
[5] =
sub
{
# assert/1
my
(
$self
,
$term
,
$c
) =
@_
;
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220$PRIMITIVES
[9] =
sub
{
# listing/1
my
(
$self
,
$term
,
$c
) =
@_
;
my
$predicate
=
$term
->getarg(0)->getfunctor;
$self
->{_db}->list(
$predicate
);
return
CONTINUE;
};
$PRIMITIVES
[10] =
sub
{
# print/1
my
(
$self
,
$term
,
$c
) =
@_
;
AI::Prolog::Engine::_print(
$term
->getarg(0)->to_string );
return
CONTINUE;
};
$PRIMITIVES
[11] =
sub
{
# println/1
my
(
$self
,
$term
,
$c
) =
@_
;
AI::Prolog::Engine::_print(
$term
->getarg(0)->to_string .
"\n"
);
return
CONTINUE;
};
$PRIMITIVES
[12] =
sub
{ AI::Prolog::Engine::_print(
"\n"
); CONTINUE };
# nl
$PRIMITIVES
[13] =
sub
{
# trace. notrace.
my
(
$self
,
$term
) =
@_
;
$self
->{_trace} =
$term
->getfunctor eq
'trace'
;
AI::Prolog::Engine::_print(
'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;
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
for
( 1 ..
$columns
) {
push
@row
=>
@predicates
?
shift
@predicates
:
''
;
}
$HELP_OUTPUT
.=
sprintf
$format
=>
@row
;
$HELP_OUTPUT
.=
"\n"
;
}
$HELP_OUTPUT
.=
"\n"
;
}
AI::Prolog::Engine::_print(
$HELP_OUTPUT
);
CONTINUE;
};
$PRIMITIVES
[32] =
sub
{
# help/1
my
(
$self
,
$term
,
$c
) =
@_
;
my
$predicate
=
$term
->getarg(0)->to_string;
_load_builtins();
if
(
my
$description
=
$DESCRIPTION_FOR
{
$predicate
} ) {
AI::Prolog::Engine::_print(
$description
);
}
else
{
AI::Prolog::Engine::_print(
"No help available for ($predicate)\n\n"
);
$PRIMITIVES
[31]->();
}
CONTINUE;
};
my
$gensym_int
= 0;
$PRIMITIVES
[33] =
sub
{
# gemsym/1
my
(
$self
,
$term
,
$c
) =
@_
;
my
$t2
= Term->new(
'v'
.
$gensym_int
++, 0 );
return
$t2
->unify(
$term
->getarg(0),
$self
->{_stack} )
lib/AI/Prolog/Engine/Primitives.pm view on Meta::CPAN
445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474};
sub
find {
return
$PRIMITIVES
[
$_
[1] ] }
1;
__END__
=head1 NAME
AI::Prolog::Engine::Primitives - The code for running aiprolog builtins
=head1 SYNOPSIS
my $builtin = AI::Prolog::Engine::Primitives ->find($builtin_id);
=head1 DESCRIPTION
This module contains the code to handle the built-in predicates. The
L<AI::Prolog::Engine|AI::Prolog::Engine> assigns many builtins an ID
number and this number is used to lookup the sub necessary to execute
the built-in.
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
=head1 COPYRIGHT AND LICENSE
lib/AI/Prolog/Introduction.pod view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253=head1 NAME
AI::Prolog::Introduction - The what and the why of logic programming.
=head1 REVISION
$Id: Introduction.pod,v 1.2 2005/02/20 18:27:55 ovid Exp $
=head1 RATIONALE
You can skip this if you already know logic programming.
Note that most of this was pulled from my write-up about logic programming in
Perl at L<http://www.perlmonks.org/?node_id=424075>.
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
(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)
=item * If it's appended to list Y:
lib/AI/Prolog/Introduction.pod view on Meta::CPAN
888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142append(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:
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) {
Dumper(
$result
->X);
# array references
Dumper(
$result
->Y);
}
sub
append_prog {
return
<<
' END_PROLOG'
;
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
}
=head1 SEE ALSO
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
X-Prolog: L<http://www.iro.umontreal.ca/~vaucher/XProlog/>
Roman BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
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.
=head1 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.
lib/AI/Prolog/KnowledgeBase.pm view on Meta::CPAN
12345678910111213141516171819202122package
AI::Prolog::KnowledgeBase;
$REVISION
=
'$Id: KnowledgeBase.pm,v 1.5 2005/06/25 23:06:53 ovid Exp $'
;
$VERSION
=
'0.02'
;
use
strict;
use
warnings;
sub
new {
my
$self
=
bless
{
ht
=> {},
primitives
=> {},
# only uses keys
oldIndex
=>
""
,
} =>
shift
;
lock_keys
%$self
;
return
$self
;
}
lib/AI/Prolog/KnowledgeBase.pm view on Meta::CPAN
238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
$head
=
$head
->next_clause;
}
}
1;
__END__
=head1 NAME
AI::Prolog::KnowledgeBase - The Prolog database.
=head1 SYNOPSIS
my $kb = KnowledgeBase->new;
=head1 DESCRIPTION
There are no user-serviceable parts inside here. See L<AI::Prolog|AI::Prolog>
for more information. If you must know more, there are a few comments
sprinkled through the code.
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
This work is based on W-Prolog, L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>,
by Dr. Michael Winikoff. Many thanks to Dr. Winikoff for granting me
permission to port this.
=head1 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.
lib/AI/Prolog/Parser.pm view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132package
AI::Prolog::Parser;
$REVISION
=
'$Id: Parser.pm,v 1.9 2005/08/06 23:28:40 ovid Exp $'
;
$VERSION
=
'0.10'
;
use
strict;
use
warnings;
use
Regexp::Common;
# debugging stuff
use
Clone;
use
aliased
'https://metacpan.org/pod/AI::Prolog::Parser::PreProcessor">AI::Prolog::Parser::PreProcessor'
;
use
aliased
'https://metacpan.org/pod/AI::Prolog::TermList::Primitive">AI::Prolog::TermList::Primitive'
;
my
$ATOM
=
qr/[[:alpha:]][[:alnum:]_]*/
;
sub
new {
my
(
$class
,
$string
) =
@_
;
my
$self
=
bless
{
_str
=> PreProcessor->process(
$string
),
_posn
=> 0,
lib/AI/Prolog/Parser.pm view on Meta::CPAN
133134135136137138139140141142143144145146147148149150151152153# we need to revisit this if it breaks
# XXX Update: There was a subtle bug. I think
# I've nailed it, though. The string index was off by one
sub
getname {
my
$self
=
shift
;
$self
->{_start} =
$self
->{_posn};
my
$getname
;
if
(
$self
->current =~ /['"]/ ) {
# Normally, Prolog distinguishes between single and double quoted strings
my
$string
=
substr
$self
->{_str} =>
$self
->{_start};
$getname
= extract_delimited(
$string
);
$self
->{_posn} +=
length
$getname
;
return
substr
$getname
=> 1,
length
(
$getname
) - 2;
# strip the quotes
}
else
{
my
$string
=
substr
$self
->{_str} =>
$self
->{_start};
(
$getname
) =
$string
=~ /^(
$ATOM
)/;
$self
->{_posn} +=
length
$getname
;
return
$getname
;
lib/AI/Prolog/Parser.pm view on Meta::CPAN
198199200201202203204205206207208209210211212213214215216217218
return
(
$term
,
$string
);
}
# handle errors in one place
sub
parseerror {
my
(
$self
,
$character
) =
@_
;
my
$linenum
=
$self
->linenum;
croak
"Unexpected character: ($character) at line number $linenum"
;
}
# skips whitespace and prolog comments
sub
skipspace {
my
$self
=
shift
;
$self
->advance
while
$self
->current =~ /[[:space:]]/;
_skipcomment(
$self
);
}
# XXX Other subtle differences
sub
_skipcomment {
my
$self
=
shift
;
if
(
$self
->current eq
'%'
) {
lib/AI/Prolog/Parser.pm view on Meta::CPAN
240241242243244245246247248249250251252253254255256257258259}
# reset the variable dictionary
sub
nextclause {
my
$self
=
shift
;
$self
->{_vardict} = {};
$self
->{_varnum} = 0;
}
# takes a hash and extends it with the clauses in the string
# $program is a string representing a prolog program
# $db is an initial program that will be augmented with the
# clauses parsed.
# class method, not an instance method
sub
consult {
my
(
$class
,
$program
,
$db
) =
@_
;
$db
||= KnowledgeBase->new;
my
$self
=
$class
->new(
$program
);
$self
->linenum(1);
$self
->skipspace;
lib/AI/Prolog/Parser.pm view on Meta::CPAN
471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
}
return
$term
;
}
1;
__END__
=head1 NAME
AI::Prolog::Parser - A simple Prolog parser.
=head1 SYNOPSIS
my $database = Parser->consult($prolog_text).
=head1 DESCRIPTION
There are no user-serviceable parts inside here. See L<AI::Prolog|AI::Prolog>
for more information. If you must know more, there are a few comments
sprinkled through the code.
=head1 SEE ALSO
W-Prolog: L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>
Michael BartE<225>k's online guide to programming Prolog:
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
This work is based on W-Prolog, L<http://goanna.cs.rmit.edu.au/~winikoff/wp/>,
by Dr. Michael Winikoff. Many thanks to Dr. Winikoff for granting me
permission to port this.
=head1 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.
lib/AI/Prolog/Parser/PreProcessor.pm view on Meta::CPAN
1234567891011121314151617181920212223242526272829303132333435363738394041424344package
AI::Prolog::Parser::PreProcessor;
$REVISION
=
'$Id: PreProcessor.pm,v 1.2 2005/08/06 23:28:40 ovid Exp $'
;
$VERSION
=
'0.01'
;
use
strict;
use
warnings;
use
aliased
'https://metacpan.org/pod/AI::Prolog::Parser::PreProcessor::Math">AI::Prolog::Parser::PreProcessor::Math'
;
sub
process {
my
(
$class
,
$prolog
) =
@_
;
# why the abstraction? Because I want DCGs in here, too. Maybe
# other stuff ...
$prolog
= Math->process(
$prolog
);
return
$prolog
;
}
1;
__END__
=head1 NAME
AI::Prolog::Parser::PreProcessor - The AI::Prolog Preprocessor
=head1 SYNOPSIS
my $program = AI::Prolog::Parser::Preprocessor->process($prolog_text).
=head1 DESCRIPTION
This code reads in the Prolog text and rewrites it to a for that is suitable
for the L<AI::Prolog::Parser|AI::Prolog::Parser> to read. Users of
L<AI::Prolog||AI::Prolog> should never need to know about this.
=head1 AUTHOR
Curtis "Ovid" Poe, E<lt>moc tod oohay ta eop_divo_sitrucE<gt>
Reverse the name to email me.
=head1 COPYRIGHT AND LICENSE
Copyright 2005 by Curtis "Ovid" Poe