Vision Class: List

Overview

The List class is an indirect subclass of the Collection class which is also a superclass of the IndexedList and TimeSeries classes. Instances of the class List consist of a collection of objects that are accessed either by position or as a set. A List is updated by appending objects to its end.

The List class has been optimized to organize and query large sets of data. A large number of the messages defined for this class have been written in Vision and can therefore be modified and expanded as needed. As always, you can define any number of new messages for the class.

The List class is a direct subclass of SequencedCollection:

  Object
     |
     Function
        |
        EnumeratedFunction
           |
           Collection
              |
              SequencedCollection
                 |
                 |-- List


List Basics

To create a List, you use the , message to append one or more elements together. For example:

  !sampleList <- 10, -3, 20, 132 ;
creates a list of four integers and saves the list as the variable named sampleList. Any type of object can be included in a list and the list can contain objects from multiple classes. For example:
  !mixedList <- 1, "xyz", TRUE, NA, -3.45 ;
creates a list of five objects representing five distinct classes. List objects respond to the message count. To count the number of elements in the list mixedList, use the expression:
  mixedList count
To create an empty list (i.e., a list with 0 elements), use the expression:
  !newList <- List new ;
  newList count printNL ;
The variable newList returns a list of 0 elements. You can then append elements to this list in the normal way. For example:
  newList, 1, 2, 3 ;
  newList count printNL ;
The variable newList is now a list of 3 elements. You can display these elements using:
  newList do: [ printNL ] ;


Note:
Notice that you do not have to reassign the results back into the variable newList. The ',' message changes the recipient List object directly. The expression:
    newList, 10 ;
appends the number 10 as the fourth element of newList.

The asList message can be used to produce a 1 element list from a single object. For example:

  !myList <- 10 asList ;
  myList count printNL ;
produces a list object that contains 1 element, the integer 10. You can append additional objects to myList in the normal way.

Individual objects in a list can be accessed directly using the at: message. For example:

  1, 10, 3, 9 at: 2 . print ;
returns the second element of this list. In this case, the integer 10 is returned and printed. The at0: message works in the same manner, counting the first element as 0. For example:
  1, 10, 3, 9 at0: 2 . print ;
returns and prints the value 3.

The message sequence can be sent to an Integer to generate a list of consecutive integers starting from 1 and ending with the value of the recipient integer. For example:

  !list5 <- 5 sequence ;
creates a list containing the integers 1 through 5. This list is identical to:
  !list5alt <- 1, 2, 3, 4, 5 ;
The message sequence0 can be used to generate a list of consecutive integers starting with 0. For example:
  3 sequence0
and
     0, 1, 2
produce lists with the same elements in the same order.

The instanceList message can be sent to an object and returns a List containing all the instances in the recipient's class. Collection messages such as do:, send:, and extendBy: apply to List objects. For example:

  Currency instanceList
  do: [ displayInfo ] ;
generates a list of all currency instances and displays basic information for each element.

The send: message provides a useful technique for creating a list of named entities. To create a list of the currency objects named USD, CAD, and DEM, use:

  !currencyList <- Named Currency 
      send: [ USD, CAD, DEM ] ;
  currencyList do: [ displayInfo ] ;
The send: message returns the result of evaluating the Block supplied as its parameter. In this case, the result of sending the block to the Named Currency dictionary object is a List of Currency objects. This is equivalent to:
  !currencyList <- 
      Named Currency USD,
      Named Currency CAD,
      Named Currency DEM ;


Collection Message Summary

Since the List class is a subclass of Collection, all messages defined for the Collection class are available to List objects as well. A subset of these messages are documented in Vision Class: Collection and are summarized below.

Collection Basics
count
do:
basicDo:
send:
basicSend:
extendBy:
basicExtend:
collect:
numberElements
linkElements

Creating Subsets
select:
first:
last:

Sorting and Ranking Collections
sortUpBy:then:
sortDownBy:then:
rankUp:
rankDown:
rankDown:usingCollector:
rankUp:usingCollector:

Grouping Collections
groupedBy:
groupedByString:
groupedBy:in:
groupedBy:intersect:
groupedBy:union:
groupedBy:usingCutoffs:
groupedByCriteria:
groupPrintUsing:
mgroupedBy:

Collection Computation Messages
average
average:
average:withWeights:
compound
compound:
correlate:with:
gMean
gMean:
harmonicMean
harmonicMean:
harmonicMean:withWeights:
max
max:
median
median:
min
min:
mode
mode:
product
product:
rankCorrelate:with:
regress:
stdDev
stdDev:
total
total:

Intra-List Messages
decileUp:
decileDown:
quintileUp:
quintileDown:
percentileUp:
percentileDown:
tileUp:tiles:
tileDown:tiles:
decileUp:using:
decileDown:using:
quintileUp:using:
quintileDown:using:
percentileUp:using:
percentileDown:using:
tileUp:using:tiles:
tileDown:using:tiles:
runningTotal:
runningAverage:
normalize:
weightedDecile:
weightedQuintile:

Inter-List Messages
isEquivalentTo:
union:
union:using:
intersect:
intersect:using:
exclude:
exclude:using:
difference:

Creation and Update Messages
copyListElements
append:
collectListElementsFrom:

Inquiry Messages
all:
any:
excludesElement:
includesElement:


Additional List Messages

You can redefine Collection messages and define new messages directly for the List class. You can define any number of new methods and constants. You cannot define and update properties for List objects.

The following messages are used to create and update List objects:

Message Definition Sample
, Appends parameter to recipient, returning modified recipient 3 sequence , 10
append: Appends elements in parameter collection to recipient, returning new List 3 sequence append: 4 sequence .
appendListElementsFrom: Appends elements in parameter collection to recipient, returning modified recipient 3 sequence appendListElementsFrom: 4 sequence .
at:put: Replaces the object stored at position parameter1 in recipient with object supplied as parameter2 5 sequence at: 3 put: "xyz" .

The following messages are used to access List objects positionally:

Message Definition Sample
at: Return object in recipient at position indicated by parameter, a number; returns NA if parameter greater than recipient count 4 sequence at: 3 .
at0: Return object in recipient at 0-based position indicated by parameter, a number; returns NA if parameter greater than recipient count 4 sequence at0: 3 .
uniformAt: Return object in recipient at position indicated by parameter, a number; returns object at position 1 if parameter greater than recipient count 4 sequence uniformAt: 5 .
uniformAt0: Return object in recipient at 0-based position indicated by parameter, a number; returns object at position 0 if parameter greater than recipient count 4 sequence uniformAt0: 5 .

These messages can be used to perform vector arithmetic between two List objects. If the parameter is a scalar, that value is applied to each element in the recipient List. If the parameter is a List, the operation is applied positionally. These messages return a new List containing the same number of elements in the recipient list. These messages assume that the recipient contains numeric values and that the parameter is a Number or a List of Numbers.

Message Definition Sample
+ Add parameter to recipient 5 sequence + 5 sequence
- Subtract parameter from recipient 5 sequence - 3
* Multiply recipient by parameter 5 sequence * 5 sequence
/ Divide recipient by parameter 5 sequence / 2


Complex List Structures

The elements of a list can be any type of object including other lists. For example, to create a list whose first element is a list of 3 elements and whose second element is a list of 10 elements, use:

  #--  Create an Empty List
  !listOfLists <- List new ;                

  #-- Append 2 list objects
  listOfLists, 3 sequence, 10 sequence ;

  listOfLists count printNL ;
The variable listOfLists refers to a list object with 2 elements. Each of these elements is also a list object, the first containing 3 elements (the numbers 1 - 3) and the second containing 10 elements (the numbers 1 - 10). To display the contents of listOfLists, use:
  #-- number the elements in the outer list
  listOfLists numberElements
  do: [ "Outer List Number: " print ;      #-- display position
        position print ;                   #--    in outer list
        " Contains " print ;               #-- display element
        count print ;                      #--   count in
        " Elements" printNL ;              #--   outer list

        #- number elements in inner list and display
        ^self numberElements               #-- ^self is optional
        do: [ " element" print ;           #-- display position
              position print ;             #--   in inner list
              ":   " print ;               #-- display value of
             printNL ;                     #--   element 
            ] ;
        newLine print;                     #-- skip line
       ] ;

A mistake often made when constructing nested lists is to use the , message to append a List object to an existing List. For example:

  !list1 <- 5 sequence ;
  !list2 <- 10 sequence ;
  !newList <- list1, list2 ;
  newList count printNL ;
The variable newList contains 6 elements: the first 5 elements are the numbers 1 through 5; the last element is a List object. This can be confirmed using:
  newList do: [ whatAmI printNL ] ;
The definition of the , message at List is to modify the recipient List by appending the object supplied as a parameter to the recipient. The result of this operation will always be a List whose count is increased by one. In the expression list1, list2 , the object represented by list2 is added to list1. The fact that the object being added is itself a List does not affect the operation.

Two messages are available too append the elements of list2 to list1, returning a single List containing the combined elements of the two lists. The appendListElementsFrom: message has been defined to append the elements in the parameter to the recipient List, modifying the recipient. The append: message copies the recipient List before appending the elements in the parameter. For example:

  #--  Create a separate list which contains the combined elements
  !newList <- list1 append: list2 ;
  #-- Print the counts
  list1 count print ; 
  newList count printNL ;

  #--  Modify list1 to contain the combined elements
  list1 appendListElementsFrom: list2 ;
  #-- Print the count
  list1 count printNL;
To create a List of the two lists, list1 and list2, define an empty List and use the , message to add the two elements:
  !newList <- List new ; 
  newList , list1, list2 ;
  newList count printNL ;
  newList
    do: [ whatAmI print; count printNL ] ;
Since the , message adds its parameter as a single object, the expression: newList, list1 returns a List of 1 element. When you send the , list2 message to this List, it returns a List of 2 elements.


List Clustering and Property Assignment

Unlike most user-defined classes, you do not usually need to look at all the instances of the built-in classes as a single list. The expressions:

  List instanceList count printNL ;
and
     10 sequence instanceList count printNL ;
display the value 1. This does not mean that there is only one List object in the database. The instanceList message actually returns the List of instances in the same physical structure or "cluster" as the recipient. By default, new instances of the List class are physically stored in separate, independent structures.

To create new instances in the same physical structure as the recipient, you need to use the clusterNew message instead of the new message to create the new List object. These messages are identical for most classes, since the default behavior is to cluster new instances. For example:

  #-- create a new list in its own cluster
  !newList <- List new ; 

  #-- a cluster contains the single list by default
  newList instanceList count printNL ;

  #-- create new lists in the same cluster
  !newList1 <- newList clusterNew ;
  !newList2 <- newList clusterNew ;
  !newList3 <- newList clusterNew ;

  #-- instanceList includes all lists in cluster
  newList instanceList count printNL ;

  #--  update the different lists in cluster
  newList1, 5, 10, 20 ;
  newList2 appendListElementsFrom: 5 sequence ;

 #--   display counts for each list in cluster
 newList instanceList numberElements
 do: [ position print ;      #- position in cluster
       whatAmI print ;       #- each element is a list
       count printNL ;       #- elements in list
     ] ;
More information about general clustering rules is available.

When you are creating lists "on-the-fly", you do not need to worry about clustering issues. Clustering is important when you are updating properties with list values.

A property can be updated to contain a List using the standard assignment messages. For example:

  #-- Define a new class and some properties
  Object createSubclass: "NewClass" ;
  NewClass defineFixedProperty: "p1" ;
  NewClass define: "ts1" ;

  #--  Update the properties with list values
  NewClass :p1 <- 10 sequence ;
  NewClass :ts1 asOf: 94 put: 4 sequence ;
  NewClass :ts1 asOf: 95 put: 5 sequence ;
For efficiency reasons, it is preferable to store lists from the same cluster when you update the same property for different instances. By convention, a "prototype list" is defined for the class. Properties are assigned to new instances in this prototype's cluster. For example:
  #--  Define a prototype list for p1 property
  NewClass define: 'p1PrototypeList' toBe: List new ;

  #-- Create new instance and update its value
  NewClass createInstance
  do: [ :p1 <- p1PrototypeList clusterNew ;
        p1 appendListElementsFrom: 5 sequence ;
      ] ;
This initialization is normally added to the initializeLocalAttributes method for the class, since this is automatically run when you create a new instance. For example:
  NewClass defineMethod:
  [ | initializeLocalAttributes | 
    ^super initializeLocalAttributes ;
    :p1 <- p1PrototypeList clusterNew ;
    ^self
 ] ;

Related Topics