TEACH FOREACH A. Sloman Nov 1982. === DOING SOMETHING WITH A SET OF DATABASE ITEMS ===================== This file assumes that you have already worked through TEACH DATABASE, and that you are familiar with the use of MATCHES to compare a list and a pattern (e.g. see TEACH MATCHES). You should be familiar with the use of the prefixes "?" and "??" in patterns, to SET the value of a variable, and "^" and "^^" to USE the value of a variable. (See TEACH ARROW.) Suppose you have a collection of facts stored in the database. You may wish to find a single item, or you may wish to find several items. PRESENT and LOOKUP (explained in TEACH DATABASE) can only find a single item. Lets demonstrate with a database of information about some people. You may find it convenient to put the following procedure into a file: define people(); [] -> database; ;;; clear the database add([joe is a man]); add([jill is a woman]); add([joe lives in london]); add([jill lives in brighton]); add([bill is a man]); add([sue is a woman]); add([bill lives in london]); add([sue lives in paris]); enddefine; (You could extend this procedure with more examples of the same general kind). We'll use this procedure every time we want to start off with a new database. Try it out: : database ==> : people(); : database ==> -- PRESENT FINDS ONE THING ONLY ------------------------------ We have information about several women in the database, but if you do : vars x; : present([??x is a woman])=> : x => and then do it again : present([??x is a woman])=> : x => you'll see that it always finds the same thing (provided the database has not changed in between). That's fine if you just want to get the name of any one woman. But suppose you want to find the names of all of them? -- FOREACH ITERATES SELECTIVELY OVER THE WHOLE DATABASE --------------------- The POP11 syntax word 'FOREACH' can be used to solve the problem. Try the following: : foreach [??x is a woman] in database do x => endforeach; Every time the pattern matches a database item, the instruction "x=>" will be obeyed. -- THE SYNTAX OF FOREACH ------------------------------------------ FOREACH is used in the format: FOREACH IN DO ENDFOREACH It uses MATCHES to compare each of the lists against the , and every time the match is successful it does the . When the is the DATABASE, you can leave out the 'IN....' bit. E.g. try : foreach [??x is a woman] do x => endforeach; I.e. since you don't say in WHAT list of lists, it assumes you mean in the database. Try getting foreach to print out all the names of all the MEN. -- WHERE DO THE MEN LIVE? ------------------------------------------------ Here is how you can make FOREACH print out the home towns of all the men : vars place; : foreach [??x is a man] do : lookup([^^x lives in ??place]); : [the home of ^x is ^^place] => : endforeach; Try that. Then try doing the same for all the women. Then try printing out all the people who live in london. -- MAKING A LIST OF INFORMATION FROM DATABASE ---------------------------- So far all our FOREACH loops have merely printed something out each time. Suppose we wanted a procedure which could make a list of all the women, or a list of all the men. We'd need to go through the database as before searching for things matching a certain pattern. But each time we found the appropriate item, instead of printing it out, we can add it to a list. We'll call our procedure ALLOF. It will take a list like [man] or [woman], i.e. a list containing what can follow 'is a' in a database entry. And it will use that to find corresponding individuals. Foreach such individual, it will add it to a list, which is finally to be returned as a result. Thus: define allof(type) -> out; vars person; [] -> out; foreach [?person is a ^^type] do [^person ^^out] -> out endforeach; enddefine; Type that in and try it out: : allof([man]) => : allof([woman]) => -- HOW ALLOF WORKS ------------------------------------------------- To understand this procedure you need to remember how to build lists (e.g. as in TEACH ARROW). The line [] -> out; initialises the variable OUT to contain an empty list. Then each time an item is found in the database matching the pattern [?person is a ^^type] we add the value of PERSON to all the things already in OUT [^person ^^out] and then assign the new list to be the new value of OUT -> out When ENDDEFINE is reached, i.e. the procedure finishes running, then the value of OUT is left on the stack as the result of the procedure. Look back at the procedure and make sure you can see how this description fits it. -- AN ALTERNATIVE FORMULATION -------------------------------------- POP-11 allows the percent symbol to be used in list brackets to collect together into a list all the things left on the stack in an instruction. E.g. go to pop11 and type (getting all the commas and spaces right): : [% 3, 4, 5 + 6 %] => : [% repeat 6 times [cat on mat] endrepeat %] => In each case, the instructions between the percent symbols will be obeyed, and things will be left on the stack, but [ .... ] will collect them into a list. We can use this to redefine ALLOF so that it doesn't have to explicitly build a bit of the list each time round. Call the new version ALLOF2: define allof2(type) -> out; vars person; [% foreach [?person is a ^^type] do person endforeach %] -> out enddefine; I.e. the loop between % ... % contains instructions which will put different values of PERSON on the stack, and at the end the square brackets will make a list, which is then assigned to OUT. Compare this version with the previous one. Which you use is a matter of taste. The only difference is that in the final list the items will be in a different order from the previous version. -- FINDING THE OCCUPANTS OF A TOWN ----------------------------- See if you can use the ideas just illustrated to define a procedure which takes the name of a town, e.g. [london] or [brighton], and then makes a list of names of all the people who 'lives in' the town. define occupants(place) -> list; vars person; ........ enddefine; Use a pattern something like [??person lives in ^^place] in the FOREACH loop. Test your procedure: : occupants([london])=> : occupants([brighton])=> -------------------------------------------------------------------- If you forget the format for FOREACH and you want a reminder later, you can do ENTER HELP FOREACH. Further examples of the use of FOREACH can be found in TEACH INFECT A related facility is explained in HELP WHICH