AI-Prolog
view release on metacpan
or search on metacpan
Changes
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Revision history for Perl extension AI::Prolog.
0.740 2008-11-12
t/60aiprolog.t fixed number of planned tests (RT
Fixed dependencies in some tests.
Fixed HEAD|TAIL results bug. Added test (RT
(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
|
Changes
view on Meta::CPAN
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 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.
Added several new regression tests.
Fixed bug where code was not warning on unknown predicates.
(Doug Wilson)
Fixed bug where tracing fails when it tries to print a non-
existent "next_clause" . (Doug Wilson)
retract/1 now works correctly. (Doug Wilson)
Fixed unification bug that crept in in .62.
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)
|
Changes
view on Meta::CPAN
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | 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
Added code to avoid bug in some Perl's where "undef" looks
like a number.
Major performance improvement. Now runs about 40% faster.
Added
listing.
println(X).
is(X,Y).
plus(X,Y).
|
Changes
view on Meta::CPAN
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | 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.
The AI::Prolog interface has been cleaned up and use of the supporting modules is now officially discouraged.
Massive documentation update.
0.03 Sun Jan 23, 2005
Added tests for built-in predicates.
|
MANIFEST
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | bin/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
|
MANIFEST
view on Meta::CPAN
46 47 48 49 50 51 52 53 54 55 56 57 | 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
|
META.yml
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ---
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
Clone: 0.15
Exporter::Tidy: 0.06
Hash::AsObject: 0.05
Hash::Util: 0
Pod::Usage: 1.12
Regexp::Common: 2.119
Scalar::Util: 0
Term::ReadKey: 2.21
Term::ReadLine: 1.01
|
README
view on Meta::CPAN
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | To install this module type the following:
perl Makefile.PL
make
make test
make install
DEPENDENCIES
This module requires these other modules and libraries:
aliased
Clone
Exporter::Tidy
Test::Differences
Test::MockModule
COPYRIGHT AND LICENCE
Copyright (C) 2005 Curtis "Ovid" Poe
|
bin/aiprolog
view on Meta::CPAN
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | }
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 ||= '' ;
print $OUT $results , " " ;
unless ( $results ) {
print $OUT "No\n" ;
$RESULTS = 0;
}
}
sub user_wants_more {
return unless $RESULTS ;
ReadMode 'cbreak' ;
my $key = ReadKey(0);
ReadMode 'normal' ;
|
bin/aiprolog
view on Meta::CPAN
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | aiprolog -- A simple Prolog shell using AI::Prolog.
|
bin/aiprolog
view on Meta::CPAN
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | 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]).
The shell should respond with this:
append([],[1,2,3,4],[1,2,3,4]) ;
It should then appear to hang. It's waiting for you to type a character. If
you type a semi-colon, it will attempt to resatisfy the query. If you keep
doing that until there are no more valid results left, you'll see 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]) ;
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.
|
data/sleepy.pro
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* "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
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | instructions,
look.
/* These rules describe the various rooms. Depending on
circumstances, a room may have more than one description. */
describe(bedroom) :-
lit(bedroom),
print ( 'You are in a bedroom with a large, comfortable bed.' ), nl,
print ( 'It has been a long, tiresome day, and you would like' ), nl,
print ( 'nothing better than to go to sleep.' ), nl.
describe(bedroom) :-
print ( 'You are in your bedroom. It is nice and dark.' ), nl.
describe(bed) :-
print ( 'You are in bed, and it feels great!' ), nl.
describe(den) :-
lit(den),
|
data/spider.pro
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 | % "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).
|
examples/append.pl
view on Meta::CPAN
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | $Data::Dumper::Terse = 1;
my $prolog = AI::Prolog->new( <<"END_PROLOG");
append([], X, X).
append([W|X], Y, [W|Z]) :- append(X, Y, Z).
END_PROLOG
print "Appending two lists 'append([a],[b,c,d],Z).'\n" ;
$prolog ->query( 'append([a],[b,c,d],Z).' );
while ( my $result = $prolog ->results) {
print Dumper( $result ), "\n" ;
}
print "\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) {
print Dumper( $result ), "\n" ;
}
print "\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) {
print Dumper( $result ), "\n" ;
}
|
examples/benchmark.pl
view on Meta::CPAN
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | use lib ( '../lib/' , 'lib/' ); my $prolog = AI::Prolog->new(benchmark());
my $t0 = new Benchmark;
for (1 .. 10) {
$prolog ->query( 'nrev30.' );
while ( my $result = $prolog ->results) {
print $_ , ' ' , @$result ,$/;
}
}
my $t1 = new Benchmark;
my $td = timediff( $t1 , $t0 );
print "the code took:" ,timestr( $td ), "\n" ;
sub benchmark {
return << " END_BENCHMARK" ;
append([],X,X).
append([X|Xs],Y,[X|Z]) :-
|
examples/cut.pl
view on Meta::CPAN
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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]).' );
print "Without a cut:\n" ;
while ( my $result = $prolog ->results) {
print $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
print "\nWith a cut:\n" ;
$prolog ->query( 'append(X,Y,[a,b,c,d]).' );
while ( my $result = $prolog ->results) {
print $result ;
}
|
examples/data_structures.pl
view on Meta::CPAN
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | use lib ( '../lib/' , 'lib/' ); $Data::Dumper::Indent = 0;
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) {
print Dumper( $result ->LIST1);
print Dumper( $result ->LIST2);
}
AI::Prolog::Engine->raw_results(1);
$logic ->query( 'append([X|Y],Z,[a,b,c,d]).' );
while ( my $result = $logic ->results) {
print Dumper( $result );
}
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) {
print Dumper( $result );
}
sub thief_prog {
return << ' END_PROG' ;
steals(PERP, STUFF, VICTIM) :-
thief(PERP),
valuable(STUFF),
owns(VICTIM,STUFF),
not(knows(PERP,VICTIM)).
thief(badguy).
|
examples/if_else.pl
view on Meta::CPAN
4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $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)." );
print Dumper $prolog ->results;
$prolog ->query( "steals(ovid, X)." );
print Dumper $prolog ->results;
|
examples/member.pl
view on Meta::CPAN
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | classtime(Time) :- member(Time, [morning_day1,morning_day2,noon_day1,noon_day2]).
END_PROLOG
AI::Prolog::Engine->formatted(1);
$prolog ->query( 'classroom(X).' );
while ( my $result = $prolog ->results) {
print $result ;
}
$prolog ->query( 'teacher(sally).' );
while ( my $result = $prolog ->results) {
print $result ;
}
|
examples/monkey.pl
view on Meta::CPAN
25 26 27 28 29 30 31 32 33 34 35 36 | state(P2, BP, onfloor, H)).
getfood(state(_,_,_, has )).
getfood(S1) :- perform(Act, S1, S2),
nl, print ( 'In ' ), print (S1), print ( ' try ' ), print (Act), nl,
getfood(S2).
END_PROLOG
$prolog ->query( "getfood(state(atdoor,atwindow,onfloor,hasnot))." );
$prolog ->results;
|
examples/path.pl
view on Meta::CPAN
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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;
my $results = $prolog ->results;
print Dumper( $results );
my $t1 = new Benchmark;
my $td = timediff( $t1 , $t0 );
print "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) :-
path(S, Dest, [S], Path),
|
examples/schedule.pl
view on Meta::CPAN
41 42 43 44 45 46 47 48 49 50 51 52 53 | different([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
$Data::Dumper::Indent = 0;
AI::Prolog::Engine->raw_results(1);
$prolog ->query( 'scheduler(X)' );
print Dumper $prolog ->results;
|
examples/trace.pl
view on Meta::CPAN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #!/usr/local/bin/perl -l
use lib ( '../lib/' , 'lib/' ); AI::Prolog->raw_results(0);
my $logic = Prolog->new(thief_prog());
print "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;
}
print <<"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;
}
print <<"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),
not(knows(PERP,VICTIM)).
thief( "Bad guy" ).
|
lib/AI/Prolog.pm
view on Meta::CPAN
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | Engine->formatted(0);
Engine->raw_results(1);
sub new {
my ( $class , $program ) = @_ ;
my $self = bless {
_prog => Parser->consult( $program ),
_query => undef ,
_engine => undef ,
} => $class ;
lock_keys %$self ;
return $self ;
}
sub do {
my ( $self , $query ) = @_ ;
$self ->query( $query );
1 while $self ->results;
$self ;
}
sub query {
my ( $self , $query ) = @_ ;
$query .= '.' unless $query =~ /\.$/;
$self ->{_query} = Term->new( $query );
unless ( defined $self ->{_engine} ) {
$self ->{_engine} = Engine->new( @{ $self }{ qw/_query _prog/ } );
}
$self ->{_engine}->query( $self ->{_query} );
return $self ;
}
sub results {
my $self = shift ;
unless ( defined $self ->{_query} ) {
croak "You can't fetch results because you have not set a query" ;
}
$self ->{_engine}->results;
}
sub trace {
my $self = shift ;
if ( @_ ) {
$self ->{_engine}->trace( shift );
return $self ;
}
return $self ->{_engine}->trace;
}
sub raw_results {
my $class = shift ;
if ( @_ ) {
Engine->raw_results( shift );
return $class ;
}
return Engine->raw_results;
}
my $QUOTER ;
sub quote {
my ( $proto , $string ) = @_ ;
$QUOTER = Text::Quote->new unless $QUOTER ;
return $QUOTER ->quote_simple( $string );
}
|
lib/AI/Prolog.pm
view on Meta::CPAN
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 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 ;
}
|
lib/AI/Prolog.pm
view on Meta::CPAN
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | See also Robert Pratte's perl.com article, "Logic Programming with Perl and
more examples.
|
lib/AI/Prolog.pm
view on Meta::CPAN
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | Side note: in Prolog, programs are often referred to as databases.
|
lib/AI/Prolog.pm
view on Meta::CPAN
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | my $list = AI::Prolog->list( qw/foo Bar 7 baz/ );
$prolog ->query( qq/append(X,Y,[$list])./ );
May be called on an instance (the behavior is unchanged).
|
lib/AI/Prolog.pm
view on Meta::CPAN
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | 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" ;
}
|
lib/AI/Prolog.pm
view on Meta::CPAN
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | |
lib/AI/Prolog/Article.pod
view on Meta::CPAN
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
lib/AI/Prolog/Article.pod
view on Meta::CPAN
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | The second notation allows you to consult multiple files and add all of them to
the knowledge base.
After issuing the C<consult/1> command, the shell will appear to hang. Hit
I<Enter> to continue . We'll explain this behavior in a moment.
Now that you've loaded the program into the shell, issue the following query:
?- gives(X,Y,Z).
The shell should respond:
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 print "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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | 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
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 | 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:
my $prolog ;
CHECK {
$prolog = AI::Prolog->new( do { local $/; <DATA> } );
}
$prolog ->query( 'father(WHO).' );
while ( my $results = $prolog ->results ) {
print "@$results\n" ;
}
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 |
father(Person) :-
parent(Person, _),
male(Person).
If you run this program, it will quite happily print out "father bill" and
"father tom." In fact, if you really want to see what's going on internally,
after you issue the query you can "trace" the execution:
$prolog ->query( 'father(Who)' );
$prolog ->trace(1);
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)
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | <<== 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 | 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
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 ];
$" = ', ' ;
print "[@$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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 | |
lib/AI/Prolog/Article.pod
view on Meta::CPAN
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 | 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.
|
lib/AI/Prolog/Article.pod
view on Meta::CPAN
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 | |
lib/AI/Prolog/Builtins.pod
view on Meta::CPAN
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | thief(badguy). steals(PERP, X) :-
if (thief(PERP), eq(X,rubies), eq(X,nothing)).
|
lib/AI/Prolog/Builtins.pod
view on Meta::CPAN
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | 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
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | X is 3 * (5 + 2). % is(X, mult(3, plus(5, 2))). When using math, note that C<is> is similar to Perl's assignment operator, C<=>.
This can be confusing.
X is 3 + 2.
Sets C<X> to the value of C<5>.
If C<X> is already instantiated, this goal succeeds if the value of C<X> is the
value of the result of the right-hand side of the equation. Internally, if X is not
instantiated, it looks like this:
is(5, plus(3,2)).
The C<=> operator tries to unify the left-hand side with the right-hand side:
X = 3 + 2.
If C<X> is already instantiated, this goal succeeds if the value of C<X> is the
same goal as the right-hand side of the equation. Internally, if X is not
|
lib/AI/Prolog/Cookbook.pod
view on Meta::CPAN
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
lib/AI/Prolog/Cookbook.pod
view on Meta::CPAN
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | gather(Remaining, FromList, ResultTail).
Example queries:
gather([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.
|
lib/AI/Prolog/Cookbook.pod
view on Meta::CPAN
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | 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.
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | my $self = shift ;
if ( @_ ) {
$FORMATTED = shift ;
return $self ;
}
return $FORMATTED ;
}
my $RAW_RESULTS ;
sub raw_results {
my $self = shift ;
if ( @_ ) {
$RAW_RESULTS = shift ;
if ( $RAW_RESULTS ) {
$self ->formatted(0);
}
return $self ;
}
return $RAW_RESULTS ;
}
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | _cp => undef ,
_retract_clause => undef ,
_trace => 0,
_halt => 0,
_perlpackage => undef ,
_step_flag => undef ,
} => $class ;
lock_keys %$self ;
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).
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | %or (X,Y) :- seq(X).
%or (X,Y) :- seq(Y).
once(X) :- X , !.
END_PROG
$self ->_adding_builtins(0);
};
if ($@) {
croak( "Engine->new failed. Cannot parse default program: $@" );
}
$self ->{_retract_clause} = $self ->{_db}->get( "retract/1" );
$self ->{_goal}->resolve( $self ->{_db} );
return $self ;
}
sub query {
my ( $self , $query ) = @_ ;
$self ->{_stack} = [];
$self ->{_run_called} = undef ;
$self ->{_goal} = TermList->new( $query );
$self ->{_call} = $query ;
$self ->{_goal}->resolve( $self ->{_db} );
return $self ;
}
sub _stack { shift ->{_stack} }
sub _db { shift ->{_db} }
sub _goal { shift ->{_goal} }
sub _call { shift ->{_call} }
sub dump_goal {
my ( $self ) = @_ ;
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | _print( "\n= Goals: " . $self ->{_goal}->to_string );
_print(
"\n==> Try: " . $self ->{_goal}->next_clause->to_string . "\n" )
if $self ->{_goal}->next_clause;
}
else {
_print( "\n= Goals: null\n" );
}
}
sub results {
my $self = shift ;
if ( $self ->{_run_called} ) {
return unless $self ->backtrack;
}
else {
$self ->{_run_called} = 1;
}
$self ->_run;
}
sub _run {
my ( $self ) = @_ ;
my $stackTop = 0;
while (1) {
$stackTop = @{ $self ->{_stack} };
if ( $self ->{_goal} && $self ->{_goal}->isa(Step) ) {
$self ->{_goal} = $self ->{_goal}-> next ;
if ( $self ->{_goal} ) {
$self ->{_goal}->resolve( $self ->{_db} );
}
$self ->{_step_flag} = 1;
$self ->trace(1);
}
$self ->dump_goal if $self ->{_trace};
$self ->step if $self ->{_step_flag};
unless ( $self ->{_goal} ) {
if ( $self ->formatted ) {
return $self ->_call->to_string;
}
else {
my @results = $self ->_call->to_data;
return $self ->raw_results
? $results [1]
: $results [0];
}
}
unless ( $self ->{_goal} && $self ->{_goal}{term} ) {
croak( "Engine->run fatal error. goal->term is null!" );
}
unless ( $self ->{_goal}->{next_clause} ) {
my $predicate = $self ->{_goal}{term}->predicate;
_warn( "WARNING: undefined predicate ($predicate)\n" );
next if $self ->backtrack;
return ;
}
my $clause = $self ->{_goal}->{next_clause};
if ( my $next_clause = $clause ->{next_clause} ) {
push @{ $self ->{_stack} } => $self ->{_cp}
= ChoicePoint->new( $self ->{_goal}, $next_clause , );
}
my $vars = [];
my $curr_term = $clause ->{term}->refresh( $vars );
if ( $curr_term ->unify( $self ->{_goal}->term, $self ->{_stack} ) ) {
$clause = $clause ->{ next };
if ( $clause && $clause ->isa(Primitive) ) {
if ( ! $self ->do_primitive( $self ->{_goal}->{term}, $clause )
&& ! $self ->backtrack )
{
return ;
}
}
elsif ( ! $clause ) {
$self ->{_goal} = $self ->{_goal}->{ next };
if ( $self ->{_goal} ) {
$self ->{_goal}->resolve( $self ->{_db} );
}
}
else {
my ( $p , $p1 , $ptail );
for ( my $i = 1; $clause ; $i ++ ) {
if ( $clause ->{term} eq Term->CUT ) {
$p = TermList->new( Cut->new( $stackTop ) );
}
else {
$p = TermList->new( $clause ->{term}->refresh( $vars ) );
}
if ( $i == 1 ) {
$p1 = $ptail = $p ;
}
else {
$ptail -> next ( $p );
$ptail = $p ;
}
$clause = $clause ->{ next };
}
$ptail -> next ( $self ->{_goal}->{ next } );
$self ->{_goal} = $p1 ;
$self ->{_goal}->resolve( $self ->{_db} );
}
}
else {
return unless $self ->backtrack;
}
}
}
sub backtrack {
my $self = shift ;
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | warn @_ ;
}
sub do_primitive {
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;
|
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | |
lib/AI/Prolog/Engine.pm
view on Meta::CPAN
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | 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:
my $logic = AI::Prolog->new( $prog_text );
$logic ->query( $query_text );
AI::Logic::Engine->formatted(1);
while ( my $results = $logic ->results) {
print "$results\n" ;
}
|
lib/AI/Prolog/Engine/Primitives.pm
view on Meta::CPAN
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | $p1 = $ptail = $p ;
}
else {
$ptail -> next ( $p );
$ptail = $p ;
}
$term = $term ->getarg(1);
}
$ptail -> next ( $self ->{_goal}-> next );
$self ->{_goal} = $p1 ;
$self ->{_goal}->resolve( $self ->{_db} );
return ;
}
my @PRIMITIVES ;
$PRIMITIVES [1] = sub {
my ( $self , $term , $c ) = @_ ;
_remove_choices( $self , $term ->varid );
CONTINUE;
};
$PRIMITIVES [2] = sub {
my ( $self , $term , $c ) = @_ ;
$self ->{_goal} = TermList->new( $term ->getarg(0), $self ->{_goal}-> next );
$self ->{_goal}->resolve( $self ->{_db} );
RETURN;
};
$PRIMITIVES [3] = sub {
FAIL;
};
$PRIMITIVES [4] = sub {
my ( $self , $term , $c ) = @_ ;
my $file = $term ->getarg(0)->getfunctor;
|
lib/AI/Prolog/Engine/Primitives.pm
view on Meta::CPAN
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | continue {
++ $cx ;
}
}
if ( not defined $function_ref ) {
return FAIL;
}
my ( undef , $results_ref ) = $term ->getarg(1)->to_data;
my @results = @{ $results_ref ->[0] };
eval {
no strict 'refs' ;
$function_ref ->( @results );
};
if ( my $e = $@ ) {
if ( $e =~ UNDEFINED_SUBROUTINE_ERROR ) {
return FAIL;
}
}
return CONTINUE;
|
lib/AI/Prolog/Introduction.pod
view on Meta::CPAN
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | Given Y and Z, we can infer X.
append(X, [b,c,d], [a,b,c,d]).
And finally , given Z, we can infer all X and Y that combine to form Z.
append(X,Y,[a,b,c,d]).
Note that you get all of that from one definition of how to append two lists.
You also don't have to tell the program how to do it. It just figures it out
for you.
Translating all of this into C<AI::Prolog> looks like this:
my $prolog = AI::Prolog->new(append_prog());
$prolog ->raw_results(0)
$prolog ->query( "append(X,Y,[a,b,c,d])" );
while ( my $result = $prolog ->results) {
print Dumper( $result ->X);
print Dumper( $result ->Y);
}
sub append_prog {
return << ' END_PROLOG' ;
append([], X, X).
append([W|X],Y,[W|Z]) :- append(X,Y,Z).
END_PROLOG
}
|
lib/AI/Prolog/KnowledgeBase.pm
view on Meta::CPAN
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | return $string , $number ;
}
sub put {
my ( $self , $key , $termlist ) = @_ ;
$self ->{ht}{ $key } = $termlist ;
}
sub elements { [ values %{ shift ->{ht} } ] }
sub reset {
my $self = shift ;
$self ->{ht} = {};
$self ->{primitives} = {};
$self ->{oldIndex} = '' ;
}
sub consult {
my ( $self , $program ) = @_ ;
$self ->{oldIndex} = '' ;
return Parser->consult( $program , $self );
|
lib/AI/Prolog/KnowledgeBase.pm
view on Meta::CPAN
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | my $predicate = $term ->predicate;
if ( exists $self ->{primitives}{ $predicate } ) {
carp( "Trying to retract a primitive: $predicate" );
return ;
}
my $cc ;
my $c = $self ->{ht}{ $predicate };
while ( $c ) {
my $vars = [];
my $xxx = $c ->term->refresh( $vars );
my $top = @{ $stack };
if ( $xxx ->unify( $term , $stack ) ) {
if ( $cc ) {
$cc ->next_clause( $c ->next_clause );
}
elsif ( ! $c ->next_clause ) {
delete $self ->{ht}{ $predicate };
}
else {
|
lib/AI/Prolog/Parser.pm
view on Meta::CPAN
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | $self ->{_start} = $self ->{_posn};
my $string = substr $self ->{_str} => $self ->{_start};
my ( $getnum ) = $string =~ /^( $RE {num}{real})/;
if ( '.' eq substr $getnum => -1, 1 ) {
$getnum = substr $getnum => 0, length ( $getnum ) - 1;
}
$self ->{_posn} += length $getnum ;
return $getnum ;
}
sub getvar {
my $self = shift ;
my $string = $self ->getname;
my $term = $self ->{_vardict}{ $string };
unless ( $term ) {
$term = Term->new( $self ->{_varnum}++ );
$self ->{_vardict}{ $string } = $term ;
}
return ( $term , $string );
|
lib/AI/Prolog/Parser.pm
view on Meta::CPAN
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | }
$self ->advance;
if ( $self ->current ne "/" ) {
$self ->parseerror( "Expecting terminating '/' on comment" );
}
$self ->advance;
$self ->skipspace;
}
}
sub nextclause {
my $self = shift ;
$self ->{_vardict} = {};
$self ->{_varnum} = 0;
}
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
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | my $clause = Clause->new( $head , $body );
my $adding_builtins = Engine->_adding_builtins;
$clause ->is_builtin(1) if $adding_builtins ;
$db -> $add ( $clause , $adding_builtins );
$self ->skipspace;
$self ->nextclause;
}
return $db ;
}
sub resolve {
my ( $class , $db ) = @_ ;
foreach my $termlist ( values %{ $db ->ht } ) {
$termlist ->resolve( $db );
}
}
sub _termlist {
my ( $self ) = @_ ;
my $termlist = TermList->new;
my @ts = $self ->_term;
$self ->skipspace;
if ( $self ->current eq ':' ) {
|
lib/AI/Prolog/Parser/PreProcessor/Math.pm
view on Meta::CPAN
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | my $rhs = qr/
$final_math_term
(?:
\s*
$op
\s*
$final_math_term
)*
/ x;
my $expression = qr/
(
($simple_math_term)
\s+
($compare)
\s+
($rhs)
)
(?=[,.])
/ x;
|
lib/AI/Prolog/Parser/PreProcessor/Math.pm
view on Meta::CPAN
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | <= le
> gt
>= ge
== eq
\= ne
}
);
sub process {
my ( $class , $prolog ) = @_ ;
while ( $prolog =~ $expression ) {
my ( $old_expression , $lhs , $comp , $rhs ) = ( $1, $2, $3, $4 );
my $new_rhs = $class ->_parse( $class ->_lex( $rhs ) );
my $new_expression = sprintf
"%s(%s, %s)" => $convert { $comp },
$lhs , $new_rhs ;
$prolog =~ s/\Q $old_expression \E/ $new_expression /g;
}
return $prolog ;
}
sub _lex {
my ( $class , $rhs ) = @_ ;
my $lexer = _lexer( $rhs );
my @tokens ;
while ( my $token = $lexer ->() ) {
push @tokens => $token ;
|
lib/AI/Prolog/Parser/PreProcessor/Math.pm
view on Meta::CPAN
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | sub _next_token {
my ( $tokens , $index ) = @_ ;
for my $i ( $index + 1 .. $#$tokens ) {
return $i if defined $tokens ->[ $i ];
}
}
sub _as_string { ref $_ [0] ? $_ [0][1] : $_ [0] }
sub match { shift ; shift =~ $expression }
sub _compare { shift ; shift =~ /^ $compare $/ }
sub _op { shift ; shift =~ /^ $op $/ }
sub _simple_rhs { shift ; shift =~ /^ $simple_rhs $/ }
sub _simple_group_term { shift ; shift =~ /^ $simple_group_term $/ }
sub _simple_math_term { shift ; shift =~ /^ $simple_math_term $/ }
sub _math_term { shift ; shift =~ /^ $math_term $/ }
sub _complex_rhs { shift ; shift =~ /^ $complex_rhs $/ }
|
lib/AI/Prolog/Parser/PreProcessor/Math.pm
view on Meta::CPAN
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | 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.
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | my $VARNUM = 1;
my $OCCURCHECK = 0;
sub occurcheck {
my ( $class , $value ) = @_ ;
$OCCURCHECK = $value if defined $value ;
return $OCCURCHECK ;
}
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
bound => 0,
varid => $VARNUM ++,
deref => 0,
ref => undef ,
ID => undef ,
varname => undef ,
_results => undef ,
} => $class ;
lock_keys %$self ;
return $self ;
}
sub _new_with_id {
my ( $class , $id ) = @_ ;
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
bound => 0,
varid => $id ,
deref => 0,
ref => undef ,
varname => undef ,
ID => undef ,
_results => undef ,
} => $class ;
lock_keys %$self ;
return $self ;
}
sub _new_from_functor_and_arity {
my ( $class , $functor , $arity ) = @_ ;
my $print_functor = defined $functor ? $functor : 'null' ;
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
bound => 1,
varid => 0,
deref => 0,
ref => undef ,
varname => undef ,
ID => undef ,
_results => undef ,
} => $class ;
lock_keys %$self ;
return $self ;
}
sub varnum { $VARNUM }
sub functor { shift ->{functor} }
sub arity { shift ->{arity} }
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | $self ->{deref} = 1;
$self ->{ ref } = $term ;
}
else {
croak( "AI::Prolog::Term->bind("
. $self ->to_string
. "). Cannot bind to nonvar!" );
}
}
sub unbind {
my $self = shift ;
$self ->{bound} = 0;
$self ->{ ref } = undef ;
}
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | else {
print ( $indent . "args: null" );
}
print ( $indent . "deref: " , ( $self ->{deref} ? 'true' : 'false' ) );
print ( $indent . "varid: " , $self ->{varid}, "\n" );
}
sub unify {
my ( $self , $term , $stack ) = @_ ;
foreach ( $self , $term ) {
$_ = $_ ->{ ref } while $_ ->{bound} and $_ ->{deref};
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | return 1;
}
return if $self ->occurcheck && $term ->occurs( $self ->varid );
$self -> bind ( $term );
push @{ $stack } => $self ;
return 1;
}
sub refresh {
my ( $self , $term_aref ) = @_ ;
if ( $self ->{bound} ) {
if ( $self ->{deref} ) {
return $self ->{ ref }->refresh( $term_aref );
}
else {
if ( 0 == $self ->{arity} ) {
return $self ;
}
else {
my $term = ( CORE:: ref $self )
->_new_from_functor_and_arity( $self ->{functor},
$self ->{arity} );
for my $i ( 0 .. $self ->{arity} - 1 ) {
$term ->{args}[ $i ]
= $self ->{args}[ $i ]->refresh( $term_aref );
}
return $term ;
}
}
}
unless ( $term_aref ->[ $self ->{varid} ] ) {
$term_aref ->[ $self ->{varid} ] = $self ->new;
}
return $term_aref ->[ $self ->{varid} ];
}
sub to_data {
my $self = shift ;
$self ->{_results} = {};
my @results = $self ->_to_data( $self );
return AsObject->new( $self ->{_results} ), \ @results ;
}
sub _to_data {
my ( $self , $parent ) = @_ ;
if ( defined $self ->{varname} ) {
my $varname = delete $self ->{varname};
( $parent ->{_results}{ $varname } ) = $self ->_to_data( $parent );
$self ->{varname} = $varname ;
}
if ( $self ->{bound} ) {
my $functor = $self ->functor;
my $arity = $self ->arity;
return $self -> ref ->_to_data( $parent ) if $self ->{deref};
return [] if NULL eq $functor && ! $arity ;
if ( "cons" eq $functor && 2 == $arity ) {
my @result = $self ->{args}[0]->_to_data( $parent );
my $term = $self ->{args}[1];
while ( "cons" eq $term ->getfunctor && 2 == $term ->getarity ) {
if ( $term ->{varname} ) {
push @result => $term ->_to_data( $parent );
} else {
push @result => $term ->getarg(0)->_to_data( $parent );
}
$term = $term ->getarg(1);
}
push @result => $term ->_to_data( $parent )
unless NULL eq $term ->getfunctor && ! $term ->getarity;
return \ @result ;
}
else {
my @results = $self ->functor;
if ( $self ->arity ) {
my $arity = $self ->arity;
my @args = @{ $self ->args };
if ( @args ) {
for my $i ( 0 .. $arity - 1 ) {
push @results => $args [ $i ]->_to_data( $parent );
}
}
}
return @results ;
}
}
return undef ;
}
my %varname_for ;
my $varname = 'A' ;
sub to_string {
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | return $string ;
}
my $var = $self ->{varname} || $varname_for { $self ->varid } || $varname ++;
$varname_for { $self ->varid } = $var ;
return $var ;
}
my %CVDICT ;
my $CVN ;
sub clean_up {
my $self = shift ;
%CVDICT = ();
$CVN = 0;
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | $term = $self ->new( $CVN ++ );
$CVDICT { $self } = $term ;
}
}
return $term ;
}
sub value {
my $self = shift ;
my ( $i , $res ) = ( 0, 0 );
unless ( $self ->{bound} ) {
my $term = $self ->to_string;
croak( "Tried to to get value of unbound term ($term)" );
}
return $self ->{ ref }->value if $self ->{deref};
my $functor = $self ->getfunctor;
my $arity = $self ->getarity;
if ( 'rnd' eq $functor && 1 == $arity ) {
|
lib/AI/Prolog/Term.pm
view on Meta::CPAN
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | my $query = Term->new( "steals(Somebody, Something)." ); |
lib/AI/Prolog/TermList.pm
view on Meta::CPAN
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | my $self = bless {
term => undef ,
next => undef ,
next_clause =>
undef ,
is_builtin => undef ,
varname => undef ,
ID => undef ,
_results => undef ,
} => $class ;
lock_keys %$self ;
return $self ;
}
sub _new_from_term {
my ( $class , $term ) = @_ ;
my $self = $class ->new;
$self ->{term} = $term ;
return $self ;
|
lib/AI/Prolog/TermList.pm
view on Meta::CPAN
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
my $tl = $self -> next ;
while ( $tl ) {
$to_string .= ",$indent" . $tl ->term->to_string;
$tl = $tl -> next ;
}
return $to_string ;
}
sub resolve {
my ( $self , $kb ) = @_ ;
my $predicate = $self ->{term}->predicate;
$self ->next_clause( $kb ->get( $predicate ) );
}
1;
|
lib/AI/Prolog/TermList/Primitive.pm
view on Meta::CPAN
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |