?he 'SETS1''Page %'
?fo 'Steve Hardy'- % -'25th May, 1977'
.ce2
SET MANIPULATION USING LISTS
.br
----------------------------
.br
This handout describes various small programs to manipulate
sets represented by lists.
The object of the handout is to indicate a possible use for
lists and to show what some list processing programs look like.

Consider the following:
 	: VARS JOHN;
 	:	[MALE HAPPY SINGLE BACHELOR CLERK TALL] -> JOHN;
.br
This declares a variable
JOHN
and assigns a list of six words to it. This variable is supposed
to represent some information about an individual called John.
The elements of the list are representing properties of the person
John.
One obvious question a program using this 'knowledge' about John
will want to ask is "does John have a particular property, for
example being tall?".
We can see immediately that John does have this property and that
we don't know whether he has the property, say, fat.
How do we do this?
When asked, people say something like the following:
.in+5
"Well I look at the list representing John, starting at the
left hand end and going along the list to the right, until I
came across the appropriate word. If I got to the end of the list
before finding the word, I knew the property was not in the list."
.in-5

Could we represent this process with a program? Consider the
following:
.tp6
 	: FUNCTION MEMBER(ITEM,LIST);
 	:	IF	<the list is empty>
 	:	THEN	<not there>
 	:	ELSEIF	<the item is the first element of the list>
 	:	THEN	<it is there>
 	:	ELSE	<repeat this process with the rest of the list>
 	:	CLOSE
 	: END;
.br
The is one of many possible schemas for the required function.  The
function is called
MEMBER
(because we usually talk of something being a member of a set)
and the function
MEMBER
needs two arguments, an item and a list.
At each stage of the process there are three options,
either the list is empty, or the item is the first element
of the list, or it is not!
The above schema represents these options, using
an
IF
statement.

We need to instantiate the schema.
We can find out if a list is empty by seeing if it is equal to the empty
list, that is:
 	: IF	LIST = []
.br
but how is MEMBER to indicate to its user that the item is not
in the list?
It could print a message on the teletype, for example:
 	: THEN	[%ITEM% IS NOT IN THE LIST] =>
.br
This solution is fine if we will always use
MEMBER
directly from the teletype but we should prefer to be able to
write programs in terms of
MEMBER
and to make this possible
MEMBER
must indicate the result of its search in a way that is 
understandable by a program - therefore it is not very good for it to
print out a message.
The solution usually adopted is to "return a result" (just as
the function
HD
returns a result, rather than printing its result directly).
The most sensible result for a failed search is
FALSE
(this is a POP11 object representing falseness!)
So that we have:
.tp9
 	: FUNCTION MEMBER(ITEM,LIST);
 	:	IF	LIST = []
 	:	THEN	FALSE
 	:	...
 	: END;

We now need to translate the phrase <the item is the first element
of the list> into 
POP11.
 	:	ITEM = HD(LIST)
.br
and the result of the MEMBER is to
be TRUE.

Now we come to the tricky bit, translating the phrase <repeat this
process with the rest of the list>
into
POP11.
We tell the POP11 system we want a process performed by typing
in a function call,
so in this case we are going to have to write something like
 	:	MEMBER(...,...)
.br
That is, we want the process named
MEMBER
to be performed with some arguments.
The arguments in this case are
ITEM
and
TL(LIST).
Notice that saying
 	:	MEMBER(TL(LIST))
.br
would be insufficient since the 
POP11
system has no intelligence and is unable to assume
the first argument for the
MEMBER 
process.

Our definition is now complete:
.tp9
 	: FUNCTION MEMBER(ITEM,LIST);
 	:	IF	LIST = []
 	:	THEN	FALSE
 	:	ELSEIF	ITEM = HD(LIST)
 	:	THEN	TRUE
 	:	ELSE	MEMBER(ITEM,TL(LIST))
 	:	CLOSE
 	: END;
.br
Let us try using this new function
 	: MEMBER(SINGLE,JOHN) =>
.br
This says "execute the
MEMBER
process, using 
SINGLE
and
JOHN
as arguments and then print the result of this process"
(i.e., is
John
single?). When you try this, you will discover that there is something
wrong.
The difficulty is that the word 'single' denotes a variable and
we have not specified the value of the variable
SINGLE
(we have specified the value of the variable
JOHN).
We want to put
SINGLE
into word quotes to indicate to the
POP11
system that it is the word
SINGLE
itself
(and not its value as a variable) that we want to be an argument
of the
MEMBER
process,
so the call should be
 	: MEMBER("SINGLE",JOHN) =>
.br
Can you explain how
 	: MEMBER("SINGLE","JOHN") =>
.br
does not work?
You may find it helpful to
TRACE
the
MEMBER
process, thus:
 	: TRACE MEMBER;
.br
Roughly, this tells the
POP11
system that whenever it starts a
MEMBER 
process it is to print a message indicating the arguments and whenever
a
MEMBER
process terminates it is to print another message indicating
the result.
You will see that our definition of
MEMBER 
is recursive in that it sets up several 
MEMBER
processes.
To 'turn off' tracing on a function, type:
 	: UNTRACE MEMBER;
.br
Try experimenting with
MEMBER,
creating several more  variables like 
JOHN
and checking that the
MEMBER
process does in fact work.
Try writing a function called
HAPPYMAN
which takes a single argument and returns
TRUE
if that list represents a happy man.
Here is a framework for this function:
.tp8
 	: FUNCTION HAPPYMAN(LIST);
 	:	IF	<list represents a man>
 	:	THEN	<list represents a happy thing>
 	:	ELSE	<not a happy man>
 	:	CLOSE
 	: END;
.br
.bb
Suppose we had a list of people thus:
 	: VARS PEOPLE;
 	:	[JOHN ETHEL MARY BERT] -> PEOPLE;
.br
where each of the elements of the list of people has
been declared as an ordinary variable and assigned a list
representing the appropriate person. A sensible question
one might want to ask is
"do you know of a happy person?" or "do you know of a clerk?".
The answer belong either the name of the person or 
FALSE.
Can we write a program to answer this sort of question?
Consider the following:
 	: FUNCTION FINDONE(PROP,LIST);
 	:	IF	<no more in the list>
 	:	THEN	FALSE
 	:	ELSEIF	<first element has property>
 	:	THEN	<first element>
 	:	ELSE	<repeat with rest of list>
 	:	CLOSE
 	: END;
 	: FINDONE("TYPIST",PEOPLE) =>
 	** MARY
 	: FINDONE("ANGRY",PEOPLE) =>
 	** <FALSE>
.br
Try instantiating this function.
You will need to use the function
VALOF
which, given a word, returns the thing denoted by that word (i.e.
its value).
To understand this better, declare
JOHN
as a variable and try the following
 	: VARS LIST;
 	:	[JOHN ETHEL] -> LIST;
 	: "LIST" =>
 	: LIST =>
 	: HD(LIST) =>
 	: "JOHN" =>
 	: JOHN =>
 	: VALOF(JOHN) =>
 	: VALOF("JOHN") =>
 	: VALOF(LIST) =>
 	: VALOF("LIST") =>
 	: VALOF(HD(LIST)) =>
.br
.bb
A harder task is to answer questions of the form "tell me all
the people who are clerks", for example:
 	: FINDALL("MALE",PEOPLE) =>
 	** [JOHN BERT]
.br
Here is a suitable schema for this function
 	: FUNCTION FINDALL(PROP,LIST)
 	:	IF	<list is empty>
 	:	THEN	<none have property>
 	:	ELSEIF	<first has property>
 	:	THEN	<get those of rest that have property
 	:			and add first element to that list>
 	:	ELSE	<get those of the rest thathave property>
 	:	CLOSE
 	: END;
.br
When partially instantiated, this takes
 	: FUNCTION FINDALL(PROP,LIST)
 	:	IF	LIST = []
 	:	THEN	[]
 	:	ELSEIF	MEMBER(...,...)
 	:	THEN	FINDALL(...,...) <>
 	:		<one element list of the first element>
 	:	ELSE	FINDALL(...,...)
 	:	CLOSE
 	: END;
.bb
A yet harder task is to find all the people with a set of properties, for example:
 	: FINDSET([MALE HAPPY], PEOPLE) =>
.br
The function FINDSET takes two arguments, a list of properties and a list of things -
it is to 'return` a list of those things which have ALL the given properties.
Here is a schema for FINDSET:
 	: FUNCTION FINDSET(PROPLIST, THINGLIST);
 	:	IF	<there are no things>
 	:	THEN	<none have all the properties>
 	:	ELSEIF	<first has all properties>
 	:	THEN	<add the first to all others with all properties>
 	:	ELSE	<just get the others>
 	:	CLOSE
 	: END;
.br
This becomes:
 	: FUNCTION FINDSET(PROPLIST, THINGLIST)
 	:	IF	THINGLIST = []
 	:	THEN	[]
 	:	ELSEIF	<first has all the properties>
 	:	THEN	[% HD(LIST) %]
 	:			<> FINDSET(PROPLIST, TL(THINGLIST))
 	:	ELSE	FINDSET(PROPLIST, TL(THINGLIST))
 	:	CLOSE
 	: END;
.br
The only really tricky bit is <first has all the properties>.
What we need is a function, perhaps called SUBSET, which takes two sets
(represented by lists) and sees if all the elements of the first are MEMBERs of the second.
We can then write:
 	:	ELSEIF SUBSET(PROPLIST, VALOF(HD(THINGLIST)))
.br

Here is a schema for SUBSET:
 	: FUNCTION SUBSET(LITTLESET, BIGSET);
 	:	IF	<littleset is empty>
 	:	THEN	<yes - it is a subset>
 	:	ELSEIF	<first of littleset is in bigset>
 	:	THEN	<check that rest of littleset
 	:			is a subset of bigset>
 	:	ELSE	<no - it isn't a subset>
 	:	CLOSE
 	: END;
.br
See if you can complete this function and so define FINDSET.
.bb
Here is an alternative definition of FINDSET, see if you can understand it:
 	: FUNCTION FINDSET(PROPLIST, THINGLIST);
 	:	IF	PROPLIST = []
 	:	THEN	THINGLIST
 	:	ELSE	FINDSET(TL(PROPLIST),
 	:			FINDALL(HD(PROPLIST), THINGLIST))
 	:	CLOSE
 	: END;
.bb
In the original list for JOHN (on page 1) we had the three properties
MALE, SINGLE and BACHELOR as if they were independent.
Clearly, this is not the case for some properties will depend on others.
For example, no one can be both MALE and FEMALE, all MALE and SINGLE entities are
BACHELORs etc.
We might wish to write functions to discover, use or check such knowledge.
For example:
 	: FUNCTION CHECKSET(PROPLIST, KEYPROP, THINGLIST);
 	:	IF	EQUALSET(
 	:			FINDSET(PROPLIST, THINGLIST),
 	:			FINDALL(KEYPROP, THINGLIST))
 	:	THEN	TRUE
 	:	ELSE	FALSE
 	:	CLOSE
 	: END;
.br
That is - check that the set of things with all the properties in PROPLIST
is equal to the set of things which have KEYPROP as a property.
We would expect that:
 	: CHECKSET([MALE SINGLE], "BACHELOR", PEOPLE)
.br
would return TRUE.

Define the function EQUALSET which checks that two lists have the same elements
(perhaps shuffled), for example:
 	: EQUALSET([A B C], [C B A]) =>
 	** <TRUE>
 	: EQUALSET([A B C], [A B D]) =>
 	** <FALSE>

For more on sets, see the SETS2 demo
