next up previous contents
Next: Lists vs other Up: CHAPTER.6: LIST PROCESSING Previous: On knowing about

Why use lists?

For work in Artificial Intelligence, and many other applications where we need to represent data in a variety of forms, lists are a very useful data type, partly because it is possible for two lists to share common sublists, partly because it is possible for lists to change their size by being extended in the middle or at either end, and partly because there is a way of representing them that is simple and flexible.

In Lisp and closely related languages, such as Scheme and T, it is also the case the lists can represent interpreted procedures: this is sometimes useful, though not as useful as the early inventors of Lisp supposed it would be. Pop-11 has a different philosophy regarding procedures and some of the things that a Lisp programmer would do by building a list and interpreting it, a Pop-11 programmer would do by using partial application to build closures that can be run (as described in chapter 4).

Also, the incremental compiler provided in Pop-11 allows lists of text items representing procedure definitions to be compiled at run time. For example

    vars name = "silly", func = "last", list = [a b c];
    popval([define ^name; ^func(^list); enddefine;]);
creates a new COMPILED procedure called silly, which can be run like any other procedure and then applies last to [a b c]

    silly() =>
    ** c
This is sometimes useful, in writing programs that create new programs.

However, Pop-11 does not provide the equivalent of Lisp's EVAL, which interprets Lisp procedure definitions.

Nevertheless lists play a very important role in representing information in many Pop-11 programs.

Lists can contain a mixture of elements of any type in Pop-11

Lists can be used to represent or store many kinds of information. In Pop-11, like most AI languages, lists can contain any type of object, including, for instance, numbers, words, procedures, and other lists. Moreover, the same list can contain a mixture of items of different types. This is impossible to achieve in strongly typed languages, including even some languages with a polymorphic type system (e.g. ML).

This generality has a number of implications. First of all it is possible conveniently to use lists to represent all kinds of information in a common format, especially information that changes its structure while a program is running. Moreover, because one does not need to use different datastructures for different kinds of information it is possible to produce a collection of very general re-useable procedures that work on lists containing different kinds of data. The Pop-11 "pattern matcher", described below, is an example of a very powerful general purpose utility procedure for operating on many types of lists. Another example is the Pop-11 database package, based on the matcher.

Being able to use such "generic" procedures with lists of different kinds can simplify program development and lead to more compact and robust programs.

Illustrating generality: isinlist

To illustrate here is a simple example. In Pop-11 it is possible to provide a single "isinlist" procedure, which takes an item and a list and returns a boolean result, which is true if the item is in the list otherwise false, as follows:

    define isinlist(target, list) -> found;

        lvars item;
        for item in list do
            if item = target then
                true -> found;
                return();
            endif
        endfor;
        false -> found
    enddefine;
This procedure is very general, insofar as it can then be used on lists of numbers, words, strings, lists, etc. in any combination, as it makes no assumptions about the types of the values of the variables "target" and "list", except that the value of "list" should be a list: if not a run time error will occur, because it uses the list iteration construct. Although it assumes that the second argument will always be a list, it makes no assumptions about the contents of the list. In some languages, such as Pascal it would be necessary to produce a different version of the procedure for each type of list, and it would not be possible to mix items of different types in one list.

The versatile behaviour of isinlist can be illustrated thus:

    vars person_data =
        [name joe age 23 wife mary salary 20000 children [sue fred]];

    isinlist("mary", person_data) =>
    ** <true>

    isinlist(20000, person_data) =>
    ** <true>

    isinlist("fred", person_data) =>
    ** <false>

    isinlist([sue fred], person_data) =>
    ** <true>
(In fact the built in Pop-11 procedure "member" behaves like isinlist, so it is not necessary for users to define isinlist. Try replacing "member" in all the above examples.)

There are many other examples of re-usable general procedures that operate on lists no matter what their contents. For example, applist, maplist, syssort, the concatenator <>, and other procedures described below.

The library procedure assoc can create an associative memory made of two-element lists, no matter what the contents of the lists are.

    vars person =
        assoc([[name fred]
               [sex male]
               [age 30]
               [kids [sue tom dick]]]);

    ;;; person is now an association mechanism.
    person("age") =>
    ** 30
    person("age") + 1 -> person("age");
    person("age") =>
    ** 31
    person("kids") =>
    ** [sue tom dick]


next up previous contents
Next: Lists vs other Up: CHAPTER.6: LIST PROCESSING Previous: On knowing about



Aaron Sloman
Fri Jan 2 03:17:44 GMT 1998