[comp.lang.icon] prolog2.usr

alanf@bruce.OZ (Alan Grant Finlay) (03/29/90)

                          Prolog in Icon (version 2)
                          --------------------------
                        Alan Finlay, Monash University.

The Prolog interpretor associated with this file is written in Icon for  a  very
simple  version  of  pure Prolog.  Cuts, arithmetic, assert and retract are  not
implemented.   There  is  only  one  non  logical  primitive "consult"  which is
used as if it were a fact with one argument.  The argument to consult is used as
a file name and the file is consulted  for  clauses  and queries.   Negation  is
implemented as negation by failure.  Version two includes list notation.

Acceptable Prolog programs consist of a list of clauses and queries,   one   per
line.  The clauses are either facts or rules:

        predicate.
        predicate:-predicate,...,predicate.

A fact is a predicate followed by a full stop.  The syntax of a  predicate  will
be   described   later.   A rule has a "head" on the left of the turnstile ":-",
and a "body" on the right.  The body is a  list  of  one  or   more   predicates
separated   by  commas and terminated by a full stop.  The predicates are simple
identifiers, identifiers parameterised by one or more  arguments,   or   negated
predicates:

        identifier
        identifier(argument,...,argument)
        ~predicate

An identifier is a string of letters, digits,  underline  or  full   stop.    An
identifier   which   starts  with  an  upper  case  letter  and has no arguments
associated with it is interpreted as a variable identifier.  The  arguments  are
syntactically   identical   to  predicates  but  interpreted differently.  Those
arguments with arguments of their own are called structures, those  without  are
called   constants   if  not starting with an upper case letter and variables if
they do.

A query is like a rule without a head:

        :-predicate,...,predicate.
        ?-predicate,...,predicate.

The first form causes the interpreter to find all solutions to the  query.   The
second   form   only  asks  for  one  solution.   The interpreter reports values
assigned to free variables in the  query  and  this  is  the  way   answers   to
questions more complex than yes/no are obtained.

As a simple example here is a traditional inference test program

        mortal(X):-man(X).
        man(X):-greek(X).
        greek(socrates).
        ?-mortal(socrates).

Try typing in this example after starting the interpreter with the command

        iconx prolog

and the response is

        yes

After this the interpreter will be waiting for another clause or query   to   be
entered.  Try entering another query for example

        ?-mortal(Socrates).

which produces the strange response

        yes, Socrates = socrates

This is because the upper case S indicates a  free  variable.    To   experiment
with negation try

        being(X):-man(X).
        being(X):-god(X).
        god(apollo).
        :-being(X),~mortal(X).

which produces the response

        yes, X = apollo
        no more solutions

Note that negation should only be applied to ground terms (terms with  all   the
variables bound) or strange behaviour may result.  For example the query

        :-~mortal(X),being(X).

which fails with no solutions.

Finaly send an end of file  to  finish  interpreting  Prolog   commands.    More
examples   can   be  found  in files "test1.plg", "test2.plg", . . .  To run the
first of these enter the fact

        consult(test?.plg).

etc, after starting the interpreter.

The list notation is simply  a  set  of  convenient  abbreviations.   Lists  are
assumed to be represented by using the binary dot constructor "." and "nil". The
dot constructor should  only  be  applied  to  (element,list)  pairs  and  "nil"
represents  an  empty list.  An infix version of the dot constructor "|" is also
provided and is useful for pattern matching.  Some examples follow.

        abbreviation                    represents
        []                              nil
        [1]                             .(1,nil)
        1|nil                           .(1,nil)
        [1,2]                           .(1,.(2,nil))
        [1,2,3,4]                       .(1,.(2,.(3,.(4,nil))))
        1|2|3|4|nil                     .(1,.(2,.(3,.(4,nil))))
        [[1,2],[3,4]]                   .(.(1,.(2,nil)),.(.(3,.(4,nil)),nil))
        1|2                             .(1,2)

The last example is not a proper list since  the  second  argument  of  the  dot
constuctor  is  not  a list.  The list [A,B,C,D] must have exactly four elements
whereas the list A|B|C|D has three or more depending upon the length of the list
bound to D.  The following two versions of naive reverse are exactly equivalent.

reverse([],[]).
reverse(X|Y,Z):-reverse(Y,W),append(W,[X],Z).
append(X,[],X).
append([],X,X).
append(X|Y,Z,X|W):-append(Y,Z,W).

reverse(nil,nil).
reverse(.(X,Y),Z):-reverse(Y,W),append(W,.(X,nil),Z).
append(X,nil,X).
append(nil,X,X).
append(.(X,Y),Z,.(X,W)):-append(Y,Z,W).