Search                        Top                                  Index
TEACH PROGLECT4                                    Aaron Sloman Oct 1998
Revised 6 Nov 2000

The Pop-11 database and its uses.

See TEACH * PROGLECT1, PROGLECT2, PROGLECT3,

CONTENTS

 -- The Pop-11 matcher
 -- Some simple examples showing testing the contents of a list
 -- Examples using variables to remember what was matched
 -- The matcher can also dig into embedded lists
 -- "  for use outside conditionals">The matcher arrow "-->"  for use outside conditionals
 -- The matcher can also use restrictions on variables
 -- The Pop-11 database
 -- Simple database examples
 -- Some useful database procedures
 -- Iterating over the database with foreach
 -- Examples using foreach
 -- Matching lists of patterns consistently: forevery
 -- One way to fix two_away
 -- Another way to fix the problem
 -- Using the database to do list processing
 -- Allpresent and which_values
 -- More on the database
 -- -- Query procedures
 -- Database reminders
 -- Next task: build a more convenient interface. See TEACH RIVERCHAT
 -- Some examples in the library

-- The Pop-11 matcher -------------------------------------------------

One of the features of Pop-11 which makes it particularly useful for
teaching is the pattern matcher. This allows you to access the contents
of complex list structures relatively easily.

Contrast this:

    vars list;
    [the cat sat on the very old mat] -> list;

    vars third, rest;
    if list matches ![= = ?third ??rest] then
        rest =>
        third =>
    endif;

With this

    if length(list) >= 3 then
        hd(tl(tl(list))) -> third;
        tl(tl(tl(list))) -> rest;
        rest =>
        third =>
    endif;

Code written with the pattern matcher is often much clearer and easier
to keep bug free than code using the lower level list processing
procedures like hd and tl. However it may run a bit more slowly.
(There are always tradeoffs.)

The use of the pattern matcher makes it easy to provide a simple but
powerful "database" mechanism.

For more on the matcher see
    TEACH MATCHES, TEACH MATCHES2, HELP MATCHES
    PRIMER, chapter 7

The matcher is also used in the far more powerful Poprulebase system,
described in TEACH RULEBASE.

-- Some simple examples showing testing the contents of a list --------

The matcher basically compares two lists of items in the same order:

    [1 2 3] matches [1 2 3] =>

    [1 2 3 4] matches [1 2 3] =>

    [1 2 3] matches [1 2 3 4] =>

    [1 2 3] matches [3 2 1] =>

All those examples would have worked with "=" instead of "matches", e.g.

    [1 2 3]  = [1 2 3] =>

    [1 2 3 4] = [1 2 3] =>

However "matches" is more powerful. It can also compare a list with a
"template" or "pattern".

These contain special pattern elements which allow the matching process
to be more flexible.

There are two anonymous pattern elements (jokers) which will match
anything but will not keep any record of what they have matched, unlike
pattern elements with variables that remember what they matched:

    "="     matches exactly one element
    "=="    matches an arbitrary number of elements

(The latex Max Clowes used to say: "Gobble one" vs "Gobble many").

    [1 2 3] matches [= 2 ==] =>
    [1 2 3] matches [== 2 ==] =>
    [1 2 3] matches [== 2 =] =>
    [1 2 3 4 5] matches [== 2 ==] =>
    [1 2 3 4 5] matches [== 1 2 ==] =>

A "segment" pattern element matches an arbitrary sublist (segment of
the list), including an EMPTY sublist, as in the last case.

Compare:
    [steve is a teacher ] matches [ ==  is a == ] =>
    [steve is a teacher ] matches [ ==  is == a == ] =>

    [fred is not a computer] matches [ == is a == ] =>
    [fred is not a computer] matches [ == is = a == ] =>


-- Examples using variables to remember what was matched --------------


Create a list:

    vars list = [the cat sat on the very old mat];

We now look at various ways of accessing its components using the
pattern matcher:

    vars between;

    list matches [== cat ??between old ==] =>

    between =>

    list matches [== the ??between old ==] =>

    between =>

    list matches [== = the ??between old ==] =>

    between =>

    list matches [== = the cat ??between old ==] =>

    between =>

-- The matcher can also dig into embedded lists -----------------------

    vars list = [[a stich in time] saves [twenty nine]];

    vars xxx, yyy, zzz;

    list matches [[a ??xxx time] ?yyy [??zzz]] =>

    xxx =>

    yyy =>

    zzz =>

What about this

    list matches [[a ??xxx time] ??yyy [??zzz]] =>

    xxx =>

    yyy =>

    zzz =>

and this
    list matches [[a ??xxx time] ??yyy ?zzz] =>

    xxx =>

    yyy =>

    zzz =>

and this

    list matches [[a ??xxx time] ??yyy ??zzz] =>

    xxx =>

    yyy =>

    zzz =>

Warning: two adjacent segment pattern elements will produce undefined
results.

"  for use outside conditionals">-- The matcher arrow "-->"  for use outside conditionals --------------

See TEACH MATCHES2

Note, we can use "-->" if we are not interested in WHETHER the match
holds, only what is in the list.

    vars x;
    [a b c] --> [a b ?x];

    x=>


But mishaps may occur.

    [a b c] --> [a b c d];

The syntax for pattern elements is the same whether you use "matches"
or "-->" or the database constructs based on the matcher described
below (and in Chapter 7 of the Pop-11 primer).


-- The matcher can also use restrictions on variables -----------------

    vars
        item, item2,
        testlist = ['a string' 4 cat [a list] 3 dog];


    testlist --> [== ?item:isnumber ==];

    item =>

    testlist --> [== ?item:isstring ==];

    item =>

    testlist --> [== ?item:isword ==];

    item =>

    testlist --> [== ?item:islist ==];

    item =>

    vars rest;

    testlist --> [??item:3 ??rest];

    item =>

    rest =>


For more on pattern elements and restrictions see
    HELP MATCHES

-- The Pop-11 database ------------------------------------------------

THE DATABASE AS A LIST OF LISTS OF INFORMATION

The database package provides a subset of such facilities. See

    TEACH * DATABASE

Lists are a derived data-type, based on Pop-11 pairs, together with a
host of procedures and syntax constructs that treat chained sets of
pairs as if they were one extended object, e.g.

Procedures for lists:
    hd,
    tail,
    length,
    applist,
    rev
    member
    last
    <>
    =
    matches

Syntactic constructs for use with lists, e.g.

    for <var> in <list> do .... endfor,
    for <var> on <list> do .... endfor,

See HELP FOR

Similarly Pop-11 databases are a derived data-type, based on Pop-11
lists, together with a collection of procedures and syntactic constructs
that treat a list of lists as if it were one object, e.g.

Procedures for the database:

    add,
    alladd,
    remove,
    flush,
    present,
    lookup,
    allpresent,
    which_values

Syntactic constructs for the database:

    foreach <pattern> do ... endforeach, etc.
    forevery <list of patterns> do ... endforevery, etc.

The database concept depends essentially on the use of the Pop-11
pattern matcher.

The matcher compares ONE list of information with a pattern.

The database can be thought of as containing MANY lists that can be
compared with the same pattern. This is the simplest use of the
database.

E.g. you may wish to check whether the database contains at least one
item that matches a given pattern.

Or you may wish to find ALL the items that match a pattern.

In either case, pattern variables may be used to extract information
from the items in the database, just as a pattern can extract
information from a single list.

Likewise it is possible to remove from the database an item that matches
a pattern, or to remove ALL items that match a pattern.

There are database procedures that take a pattern and compare it with
all the items in the database, or all until something is found that
matches.

There are more complex database procedures that take a LIST of
patterns and try to find all the different ways those patterns can
consistently be matched against database entries.

Thus you can find if someone is the maternal grandfather of
someone else by using a list containing these two patterns:

    [ [father ?person1 ?person2] [mother ?person2 ?person3] ]

Matching consistently means that the matcher should produce the same
values for the same variables.

I.e. after the first pattern has matched something it constrains
what can match the second pattern.

Note: rule-based programming (as explained in TEACH * RULEBASE and
related files, is a generalisation of database programming.)

So is Prolog, the logic programming language.

-- Simple database examples -------------------------------------------


    database ==>
    add([steve is a teacher]);
    add([steve likes apple pie]);
    database ==>
    add([fred is a teacher]);
    add([fred likes jam tart]);
    add([julie is a teacher]);
    add([julie likes baked haddock]);
    database ==>

Compare the simple print arrow -- the printing is not so easy to read
when the list of lists is too long to fit on one line.

    database =>


    present([steve is a teacher]) =>
    present([steve likes baked haddock]) =>
    present([aaron likes jam tart]) =>


    present( ![julie likes ??x] ) =>
    x =>
    it =>

    present( ![??x likes apple pie] ) =>
    x =>
    it =>

    vars person, what;

    present( ![??person likes ??what] ) =>


The procedure present returns a true or false result, so that it can be
used in conditionals. The procedure lookup does not return a result. It
is used only for its 'side-effects", i.e. setting variables in a pattern
to record what was found in the database, just as "-->" is used only for
its side effects whereas matches producs a true/false result.

    lookup( ![fred likes ??x] );    ;;; use semi-colon, not print arrow
    x =>

    lookup( ![mary likes ??x] );    ;;; use semi-colon, not print arrow

The procedures remove and flush also produce no result. They take a list
or a pattern and remove items from the database. Only one item, the
first one found to match the pattern, is removed by remove. By contrast
flush removes everything that matches the pattern. The item removed in
the first case is assigned to the variable it. The items removed by
flush are put in a list (which may be empty) and the list is assigned to
the variable them.

    remove([== likes ==]);
    database ==>
    it =>

    flush([== likes ==]);
    database ==>
    them ==>

If there is nothing to match, then remove produces a mishap:
    remove([== likes ==]);

    ;;; MISHAP - ATTEMPT TO REMOVE NON-EXISTENT ITEM
    ;;; INVOLVING:  [== likes ==]
    ;;; DOING    :  remove runproc ....

Whereas flush does not mishap, but if nothing in the database matches
its argument, it assigns the empty list to them.

    database ==>
    flush([== elephant ==]);
    database ==>
    them ==>

Don't confuse flush, which removes all items matching one pattern, and
allremove, which takes a list of patterns, and removes one item matching
each pattern.

    [[julie is a teacher] [fred is a pilot] [steve is a nurse]]
        -> database;

    vars list1, list2;
    allremove(![ [fred ??list1] [steve ??list2]]);

    database ==>
    them ==>
    list1 ==>
    list2 ==>

-- Some useful database procedures ------------------------------------

    add(ITEM)
        The item should be a list. It is added to the database.
        It is also assigned to the global variable "it", which is used
        for the last database item accessed.

    alladd(LIST)
        Takes a list of database items (all lists) and adds them to
        the database. It assigns the list to the global variable "them".

    present(PATTERN) -> BOOLEAN

        Tries everything till an item is found that matches
        I.e. looks for ONE thing.

        returns a true-false result

        If it finds something that matches the pattern it

            (a) sets variables in the pattern

            (b) sets the variable "it" to have the item as its value

            (c) returns immediately with a true result.

        It could be defined thus:

        define present(pattern) -> boolean;
            lvars item;

            for item in database do
                if item matches pattern then
                    item -> it;
                    true -> boolean;
                    return();
                endif;
            endfor;
            ;;; failed to find match, so return false
            false -> boolean;

        enddefine;


    foreach PATTERN do ... endforeach;

        Tries EVERY ITEM in the database

        Every time an item is found that matches PATTERN foreach

            (a) sets variables in the pattern

            (b) sets the variable "it" to have the item as its value

            (c) performs the action, using the values of the variables.


    lookup(PATTERN)
        NB This has no result. It is like "-->"

        It looks for at least ONE thing that matches the pattern.
        Generates an error if it fails

        If it succeeds
            (a) sets variables in the pattern

            (b) sets the variable "it"

        It could be defined thus

        define lookup(pattern);
            unless present(pattern) then
                mishap(pattern, 1, 'LOOKUP FAILURE')
            endunless
        enddefine;


    Example:
    alladd([ [a b c d] [d c b a] [a b d c] ]);

    vars x;
    lookup([= ?x b ==]);
    x =>
    it =>

    lookup([= ?x e ==]);

Extract from TEACH PRIMER

    procedure   returns           iterates over       mishap on
                a boolean         the database        failure
    ---------------------------------------------------------
    matches      yes                   no               no

    -->          no                    no               yes

    present      yes                   yes              no

    lookup       no                    yes              yes


    remove(PATTERN)

        Searches for one thing in the database and removes it

        If it succeeds
            (a) sets variables in the pattern

            (b) sets the variable "it"

    flush(PATTERN)
        removes everything that matches

            sets the variable "them" to be a list of items removed.

    [] -> database;
    alladd([ [a b c d] [d c b a] [a b d c] [1 b 3 d 5]]);
    database ==>
    flush([ == b == d == ]);
    them =>
    database ==>

    allremove(LIST)
        Given a list of patterns (or lists) makes sure that it can
        remove at least one item for each of them, and does so.
        If not, generates a mishap.


-- Iterating over the database with foreach ---------------------------

Formats:
    foreach <pattern> do <actions> endforeach

    foreach <pattern> in <list> do <actions> endforeach

or
    foreach ! <pattern> do <actions> endforeach
    foreach ! <pattern> in <list> do <actions> endforeach

This tries matching the <pattern> against every item in the database.
Every time the match is succesful it performs actions.

You can treat

    foreach <pattern> do <actions> endforeach

as approximately equivalent to this:

    lvars item;
    for item in database do

        if item matches <pattern> then
            <actions>
        endif;

    endfor;

Note that this uses the variable database as global.

In fact it is a bit more complicated than that because foreach ensures
that the original database is used throughout the loop since the
<actions> can change the database if there are add or remove actions
included.

-- Examples using foreach ---------------------------------------------

define people();
    ;;; clear the database and set up a collection of "initial" facts
    []  -> database;
    add([joe isa man]);
    add([jill isa woman]);
    add([joe lives_in london]);
    add([jill lives_in brighton]);
    add([bill isa man]);
    add([sue isa woman]);
    add([bill lives_in london]);
    add([sue lives_in paris]);
enddefine;

people();


We have information about several women in the database, but if you do

    vars x;
    present([?x isa woman])=>
    x =>

and then do it again
    present([?x isa woman])=>
    x =>

you'll see that it always finds the same thing (provided the database has not
changed in between).

But foreach finds them all:

    vars x;
    foreach [?x isa woman] in database do x => endforeach;

We could make a list of all of them

    vars x, list;
    [% foreach [?x isa woman] in database do x  endforeach %] -> list;

    list =>

It is possible to use nested foreach lists.

    [[connects room1 room2]
     [connects room1 room3]
     [connects room2 room4]
     [connects room3 room5]
     [connects room3 room4]
     [connects room4 room6]] -> database;

    vars connections;
    vars r1, r2, r3;
    [%
        foreach ! [connects ?r1 ?r2] do

            foreach ! [connects ^r2 ?r3] do
                [Two away ^r1 ^r3]
            endforeach

        endforeach
    %] -> connections;

    connections ==>

Note that for every match of the FIRST pattern the second one is
tried against ALL the database items.

This can be abbreviated using forevery, which takes a list of lists
in one loop.

    vars connections;
    vars r1, r2, r3;
    [%
       forevery ! [[connects ?r1 ?r2][connects ?r2 ?r3]] do
                [Two away ^r1 ^r3]
       endforevery
     %] -> connections;

    connections ==>

Forevery can be used with an arbitrarily long list of patterns. N
patterns corresponds to N nested loops. (As N increases it gets
slower and slower...).

If K is the length of the database there are N x K matches to do.

Compare which_values

    which_values( ! [?r1 ?r3], ! [[connects ?r1 ?r2][connects ?r2 ?r3]] )
        ==>

This is defind in terms of forevery, approximately like this:


define which_values(Vars, Patternlist) -> List;
    ;;; Vars should be a list of variables prefixed by "?" transformed by "!"
    ;;; Patternlist should be a list of patterns, the whole having been transformed
    ;;; by "!"


    if ispair(Vars) then
        if ispair(Patternlist) then
            [%forevery Patternlist do
                lvars list;
                [%for list on Vars do
                    if hd(list) == "?" or hd(list) == "??" then
                        valof(hd(tl(list)))
                    endif
                  endfor%]
              endforevery%] -> List
        else mishap('LIST OF PATTERNS NEEDED', [^Patternlist])
        endif;
    else
        mishap('LIST NEEDED FOR "WHICH_VALUES"', [^Vars] )
    endif;
enddefine;

-- Matching lists of patterns consistently: forevery ------------------

This example introduces the trade-off between forms of representation
and choice of algorithms.

Searching in the database for an item or set of items that match a given
pattern is a relatively trivial task. Far more complex is finding a
consistent way of matching a collection of patterns against a database.
For example you could have a database of information about which rooms
are connected with which.

    [[connects room1 room2]
     [connects room1 room3]
     [connects room2 room4]
     [connects room3 room5]
     [connects room3 room4]
     [connects room4 room6]] -> database;

Graphically

        room3 --- room1 --- room2
          | |                |
          | +----- room4 ----+
          |          |
        room5      room6

Now suppose you wanted to find routes to all the rooms that are two
links away from a given room, you can use the forevery construct in a
procedure like this:

define two_away(room) -> list;
        ;;; return a list of two-step routes from room

        lvars r1, r2;
        [%
            forevery ! [[connects ^room ?r1][connects ?r1 ?r2]] do

                [^room ^r1 ^r2]

            endforevery
        %] -> list;

    enddefine;

test it

    two_away("room1") ==>

But this procedure has a bug. What is it and how can it be fixed?

It does not find all the routes:

    two_away("room3") ==>

Why not?

-- One way to fix two_away --------------------------------------------

In deciding how to fix two_away you have to consider whether to change
the representation of information in the database. For example the
information is represented asymetrically, insofar as we have
represented the fact

     [connects room1 room3]

but not the fact

     [connects room3 room1]

which is in a sense the same fact, expressed differently. You could try
extending the database to include such "reverse" facts, e.g.

    [[connects room1 room2]
     [connects room2 room1]
     [connects room1 room3]
     [connects room3 room1]
     [connects room2 room4]
     ;;; and so on
     [connects room3 room5]
     [connects room3 room4]
     [connects room4 room6]] -> database;

and then try out the procedure two_away with the same example as before


    two_away("room3") ==>

This now gives more answers.

It would be desirable to rule out answers like this:

    [room1 room3 room1]

Or would it?

-- Another way to fix the problem -------------------------------------

Another way is to use the original database, but extend the procedure
two_away to try all the combinations explicitly:

    [[connects room1 room2]
     [connects room1 room3]
     [connects room2 room4]
     [connects room3 room5]
     [connects room3 room4]
     [connects room4 room6]] -> database;

    database ==>

define two_away_symmetric(room) -> list;
        ;;; return a list of two-step routes from room

        lvars r1, r2;
        [%
            forevery ! [[connects ^room ?r1][connects ?r1 ?r2]] do
                [^room ^r1 ^r2]
            endforevery;

            ;;; keep the same first condition, but change the second
            forevery ! [[connects ^room ?r1][connects ?r2 ?r1]] do
                [^room ^r1 ^r2]
            endforevery;

            ;;; now reverse the first condition, and try both orders
            ;;; for the second
            forevery ! [[connects ?r1 ^room][connects ?r1 ?r2]] do
                [^room ^r1 ^r2]
            endforevery;

            forevery ! [[connects ?r1 ^room][connects ?r2 ?r1]] do
                [^room ^r1 ^r2]
            endforevery;


        %] -> list;

    enddefine;

        room3 --- room1 --- room2
          | |                |
          | +----- room4 ----+
          |          |
        room5      room6

Test that:
    two_away_symmetric("room1") ==>
    two_away_symmetric("room3") ==>

That is an example of a trade-off between economy of representation and
economy of processing.


-- Using the database to do list processing ---------------------------

How do you remove repeated elements from a list of lists?

One way to "prune" the list would be to make the redundant list
temporarily the database, then flush out all repetitions, like this:

define prune_list(list) -> newlist;

    vars database = list;

    lvars item, donelist = [];

    for item in list do

        ;;; If item has been met before, ignore it
        unless member(item, donelist) then
            ;;; remove all occurrences of item
            flush(item);
            ;;; now add it back
            add(item);
            ;;; and remember it
            [^item ^^donelist] -> donelist;
        endunless;
    endfor;

    ;;; then use the database as the result

    database -> newlist;

enddefine;

try it out

    prune_list([[a b][c d][a b][d e][c d]]) ==>

    prune_list([[x y z][ y z x][a b][c d][a b][x y z][d e][c d]]) ==>


-- Allpresent and which_values ----------------------------------------

Like forevery, the procedure allpresent, and the procedure which_values
are based on the idea of taking several patterns, and looking for
consistent ways of matching them with database items.

But allpresent is more limited in that it finds only ONE such consistent
set of matches, and then stops. E.g.

    vars w,x,y,z;

    allpresent([[connects ?w ? x] [connects ?x ?y] [connects ?y ?z]]) ==>

    them ==>

which_values is sometimes useful when you just want to get the
combinations of values of some of the variables. It must be used with
"!"

    which_values(![?x ?z], ![[connects ?x ?y] [connects ?y ?z]]) ==>

Notice that the same pair of values can occur more than once in the
output list. Why?


-- More on the database -----------------------------------------------

An application of the database facilities to a simple simulated world.

    TEACH RIVER2

    TEACH RIVER2.P

These illustrate the use of the database to store and manipulate a
representation of the world, and also to answer questions.

Procedures to be written include things like the following:

    riv_setup(left_things, right_things, boat_things);

    riv_whereis(thing) -> place;

    riv_can_eat(thing1, thing2) -> boolean;

    riv_will_eat(thing1, thing2) -> result;

For reasons explained in TEACH RIVER2 every procedure has been defined
so that it either returns "ok" or a list giving a reason for a negative
answer or a failure to perform. This makes them suitable for use in an
interactive conversational program, unlike LIB RIVER, whose facilities
cause a MISHAP if things go wrong, and a mishap aborts the program.
You don't want that to happen in a conversational program.

It might also be useful in an automatic planning program to have such
procedures which give reasons for their failure, rather than simply
aborting.


Some examples of what will be found in TEACH RIVER2

You can run these examples if you do
    lib river2

riv_setup([man fox grain boat], [chicken], []);
database ==>

riv_whereis("man") =>
riv_whereis("chicken") =>


riv_can_eat("fox", "grain") =>
riv_can_eat("chicken", "grain") =>
riv_will_eat("chicken", "grain") =>

The action routines need to check preconditions, and then change
the database as needed:

riv_putin("chicken") =>

Try doing some of the commands twice, or in inappropriate conditions, to
see what happens:

riv_putin("fox") =>
riv_putin("fox") =>
database ==>
riv_getin() =>
riv_getin() =>
riv_putin("fox") =>
riv_takeout("fox") =>
riv_getout() =>
riv_getout() =>
riv_takeout("fox") =>
database ==>


COMPLETE LIST OF RIVER2 PROCEDURES

    riv_start()
        Set up the initial state of the river.

    riv_putin(thing) -> result
        Man puts the thing into the boat

    riv_takeout(thing) -> result
        Man takes the thing out of the boat onto the river bank

    riv_getin() -> result
        Man gets into the boat

    riv_getout() -> result
        Man gets out of the boat

    riv_cross() -> result
        Man rows the boat across the river

    riv_eat(thing1, thing2)
        Something eats something else. (This produces no result because
        its preconditions will always be satisfied when it is invoked.)

-- -- Query procedures

    riv_whereis(thing) -> place

    riv_is_at(thing, place) -> boolean      (true or false result)

    riv_is_in(thing1, thing2) -> boolean

    riv_will_eat(thing1, thing2) -> boolean

    riv_which_at(place) -> list of things

    riv_which_in(thing) -> list of things

-- Database reminders -------------------------------------------------

The database package comes with a collection of different facilities:

Look at the uses of
    add
    addall
    remove
    flush
    present
    foreach
    forevery

Most of those are illustrated in TEACH RIVER2.

Contrast
    present
        Takes a pattern and searches for an item in the database, and
        stops after finding one. It is a procedure.

    foreach
        Takes a pattern and matches it against each item in the database
        in turn, and performs an action whenever there's a match.
        this is a syntax word, with corresponding brackets "do"
        and "endforeach"

    lvars thing;

    foreach ! [at ?thing left] do [^thing is at left] => endforeach;


Contrast
    foreach
        takes just one pattern

    forevery
        takes a list of patterns and tries to find consistent ways
        of matching them against database items. It is also a
        syntax word with "do" and "endforeach" brackets.

Forevery is a very powerful addition, allowing different combinations of
patterns to be matched simultaneously against different items in the
database. This does a form of inference, or reasoning. It is a step
towards the power of prolog.

Here's a simple example: work out which things are at the same place:

vars x, y, place;

forevery [[at ?x ?place][at ?y ?place]] do

    if x /= y then
        [^x ^y  'are together at' ^place] =>
    endif;

endforevery;

Change the database and try the above again:

riv_getin()=>
riv_cross() =>
riv_getout() =>

forevery is a lot more powerful.

-- Next task: build a more convenient interface. See TEACH RIVERCHAT --

Controlling program

    define riverchat();
        river_start_chat();
        river_introduction();
        river_converse();
        river_farewell();
    enddefine;


    define river_converse();                ;;; no inputs and no results
        lvars input;

        repeat
            [What next?] =>

            readline() -> input;

        quitif( input = [bye] );            ;;; stopping condition

            river_interpret(input);         ;;; still to be defined

        endrepeat;

        [good bye] =>
    enddefine;


Note the use of readline to get information from the user. What
does readline do?

TEACH READLINE

Note that in the editor it suspends the running process till you press
RETURN in the interactive file.

    vars list;
    'Please type something' =>
    readline() -> list;
    [why did you say ^^list] =>

To define the interpret procedure you need to think very clearly about
what the Ontology of the domain is, i.e. what kinds of facts can exist,
including what relationships can be talked about. Here are some
examples:

    [ the ??obj is ??rel the ??loc ]

        the fox is at the left bank

    [ where is the ??obj ]

    [ what is ??rel the ??loc ]

        what is at the right bank

    [ is the ??obj ??rel the ??loc ]

        is the fox at the right bank

    [ put the ??obj ??rel the ??loc ]

        put the fox in the boat

And many more. If you can produce a grammar, that is a good way to
generate a very large set of cases in a systematic way, but then you
have to interpret a parse tree

    [s [vp
            [vnpinnp put]
            [np the ... ]
            [prep in]
            [np the ... ]]]

-- Some examples in the library ---------------------------------------

In an Xterm window try

    pop11 +gblocks

It's not easy to make such things work!


TEACH * SCHEMATA

Introduces the task of matching a story (represented as a set of facts
in the database) against a set of scripts or frames to find the "best"
match. This can then be used to predict items of information missing
from the story.


--- $poplocal/local/teach/proglect4
--- Copyright University of Birmingham 2000. All rights reserved. ------