Setting up a database of 'facts'
The
basic idea
Imagine that you have decided to collect together a database of
facts, each of which is expressed as a perfectly ordinary English
sentence, such as Pigs can fly. (O.K. So what is a fact? I can
fly to Spain, can't I?. Indeed, I can fly to Spain more easily than I can walk to
London.) In order to take advantage of and manage such a collection of
information, it is clear that at the very least you will need to be able to:
- interrogate the database
- add new facts to the database
- remove facts
from the database
Every so often you will probably also want to:- clear
out an existing database and create a fresh one.
In this and the next linked
page, we are going to set up the bare bones of a Logo system which is aimed at meeting
these requirements through the use of four simple commands. No new Logo concepts are
introduced at this stage, though various other pages (see Better
interface and Eliza)
offer suggestions for developing aspects of the system which require more
knowledge of the language. All of the facts we will use as examples of database
entries will be single clause sentences - i.e. they will contain only one main verb.
You might find it best to maintain such a restriction given the limitations of the
system, but there are no consequences - whatever your decision - for the way in which
the component procedures work.
Representing the facts
If each fact (or record) in
the database is to have the form of - and be displayed as - an English sentence, the
most obvious Logo representation for a fact is a simple list of words,
like [pigs can fly]. On the further assumption that there is no compelling
reason to store the various facts in subgroups - since it is arguably the business of
the search mechanisms to extract sets of related facts - the database as a whole can
in turn be treated as a single list of facts:
[[roses are red][violets are blue][sugar is sweet] . . .]
Creating an empty database
Preparing a new database under these conditions is
quite straighforward. Suppose we call the entire database list "facts.
To set up an empty database, to which new facts can then be added, we need only use
make:
make "facts []
If we plan to use just one database at a time and it is always called facts,
then this instruction will obviously serve both to create a fresh database
and kill off an existing one. In these circumstances, it might even be worth
using the instruction as the only line in the definition of a procedure, called
cleanup, for example, so that the user can easily reset the database with a
single-word command:
to cleanup
make "facts []
end
Adding new facts
Although we have assumed that the order of the facts within
the database as a whole is of no importance, the facts must inevitably occur in some
particular order in the list called "facts. In other words, when we add a
new fact to :facts it has to go somewhere not just anywhere. Given the way
in which Logo handles lists, and the primitives which are normally provided, there are
two clear candidate positions for a new fact. It can be placed at the head of the
current list of facts by using fput or at the end by using lput. For
our present purpose the choice is arbitrary. But at this point, whichever you choose,
you need to take care.
It is easy to imagine that if you want to add the fact
[birds can fly] as the first fact in :facts then all you need say to
Logo is:
fput [birds can fly] :facts
Reading this line as if it were pseudo English,
however, is a bad idea. fput [birds can fly] :facts does not result in the
modification of :facts. Worse, when you try typing it in at toplevel, you will
get an error message because it is not even an instruction.
The problem is that
fput is an operation which simply provides an output on the basis of the
inputs it receives. In this case it would output a list composed of :facts
(whatever the thing called "facts is currently) with [birds can fly]
added at the beginning. The error is that you don't say what to do with this output.
And on top of that, :facts is not updated because fput, like any other
operation, does not modify its inputs, it merely uses them. If you want to
change the value of :facts you need to say so. If, in particular, you
want to have :facts take as its value the list which is output by fput
[birds can fly] :facts then you must say:
make "facts fput [birds can fly]
:facts
This now is an instruction - it begins with a command - and
there is no error because the output of fput is used as an input make.
A command to help the user add new facts
It would, of course, be tedious to
have to add every new fact to the database with an instruction as long as this. We
can simplify the updating process, just as we simplified the the creation of a fresh
database, by defining a more convenient command to be used at toplevel. After all,
from the user's point of view, given that only one database is involved, the business
of adding new information should be as simple as saying: Remember FACT.
(Where FACT is some sentence.) To approach that ideal, we can define a procedure -
let's call it learn, because learn is even easier to type than
remember - which will take a single list, such as [emus are birds], as
its input. To extend the database, the user will then need to type no more than:
learn [emus are birds]
Because of the square brackets, this is not quite English, but at least it is nearly
there.
The body of learn must obviously parallel directly the form of the instruction
make "facts fput [birds can fly] :facts which we used previously. The only
change needed within the procedure definition is that, in order to generalise the
process, the specific [birds can fly] must be replaced by a variable. Since
the input to learn is always a list representing a fact, we might as well
call the input variable "fact. The definition of learn, then, is:
to learn :fact
make "facts fput :fact :facts
end
Removing unwanted facts
Given that we can now say learn [. . . .], to
install a fact in our database, it would be nice to be able to match that command with
another called forget, which we could use to get rid of any
mistakes which might accidentally creep in. Now, if we had access to
some procedure remove which would accept two inputs, the first the unwanted
item and the second the thing to remove it from, and would output the result of
removing the first from the second, then forget could be defined following
exactly the pattern of learn:
to forget :fact
make "facts remove :fact :facts
end
As it happens the Toolbox contains a definition of an operation remove
which fits the bill exactly and all we need do to make forget work is to borrow
it. Notice again, however, the consequence of the fact that Logo operations do not alter the values of their inputs. It might seem
natural to interpret remove :fact :facts as an instruction to create a new
version of facts which no longer includes fact. This is not, however,
what happens. (Compare the comments on fput, above.) Remove, like other operations, works with copies of whatever
objects are passed to it, and identifies them by its own names. Remove
simply outputs a list formed by removing a copy of the first object passed to
it from a copy of the second object passed to it. Remove :fact :facts
leaves the original fact and facts
untouched. In order to update facts in forget it is
thus necessary to assign to facts, as its new value, the object output
by remove. Hence the form of the line: make "facts
remove :fact :facts.
Top down tactics
Notice also that the strategy that we followed in
arriving at a definition for forget - i.e. taking for granted the existence of
remove and treating it as a black box with clear functionality even if unknown
mechanisms - does not depend on the pre-existence of the procedure. If remove
did not exist, it would always be possible to define it later. This tactic of
temporarily presupposing the availability of lower level procedures - whose functions
you are clear about and whose whose names you freely invent - so that you can focus
more clearly on the essential structure of some higher level task is a generally
useful one which will repay cultivation. It is one aspect of the top-down approach to
programming which Logo encourages (but - see later - does not demand). Delegation of
resposibility to subordinates is notable feature of the management culture of theLogo
microworld. Individual Logo procedure definitions are, as a result, amost always very
simple affairs.
Link to interrogation techniques.
Ron Brasington
Department of Linguistic Science
The University of Reading
Reading
UK
E-mail: ron.brasington@rdg.ac.uk