Actor Tutorial -------------- Copyright (c) 1989 The Whitewater Group, Inc. Notes on Dictionaries --------------------- One of the most powerful features of Actor(R) is the Dictionary class. A dictionary is a collection of key/element pairs, called associations. While arrays in Actor and other languages limit you to integer keys, an Actor dictionary can use any object for its keys. The dictionary is a lookup table that gives you an element given a key to that element. Unlike English language dictionaries, an Actor dictionary has no inherit order. Basic messages that a dictionary understands include add, at, and remove. Try the following: First, create a dictionary of size 3 with a new message: Locale := new(Dictionary, 3); This is only an initial size; dictionaries automatically grow as you add elements. Add two associations to the dictionary, "Here"->"Belize" and "There"->"Akron." In this example, "Here" and "There" are the keys; "Belize" and "Akron" are the elements: add(Locale, "Here", "Belize"); add(Locale, "There", "Akron"); Now find what element is referenced by the key "There": at(Locale, "There"); Finally, remove the association at "Here": remove(Locale, "Here"); You will notice that at and add messages return the element; remove returns the key. You can examine the dictionary as you alter it by typing Locale in the workspace or by using an inspector. There is a syntactic shorthand for sending add and at messages: the familiar "[ ]" notation used with arrays. The message... Is equivalent to... add(Locale, "There", "Akron") Locale["There"] := "Akron" X := at(Locale, "Here") X := Locale["Here"] In the above example, both the keys and elements are strings. Remember that any object can be a key and any object can be an element in a dictionary. You may want to use arbitrary integers or characters as keys. You may want to have Point objects, strings, or even other dictionaries as elements. You can do much more than send add, at, and remove messages to a dictionary; Actor gives you the power to perform operations on each item of a dictionary. The do methods enumerate over every element of a collection; in other words, a do returns a value for each entry in turn, allowing you to perform a set of operations on each. There are three do messages which are sent to dictionaries: do, keysDo, and assocsDo. Each operates in a slightly different fashion, as shown here. Recreate the Locale dictionary used above for these examples: Locale["Here"] := "Belize"; Locale["There"] := "Akron"; do Enumerate each element in the dictionary: do(Locale, {using(anElem) printLine(anElem)}); The display reads: Belize Akron keysDo Enumerate each key in the dictionary: keysDo(Locale, {using(aKey) printLine(aKey)}); The display reads: Here There assocsDo Enumerate each key/element pair in the dictionary: assocsDo(Locale, {using(anAssoc) printLine(anAssoc)}); The display reads: "Here"->"Belize" "There"->"Akron" This indicates that the entire association was returned with assocsDo. When using the various do methods, it is helpful to use a meaningful name for the temporary variable; in this case, aKey, anElem, and anAssoc were used as a reminder of their contents. You may find it useful to locate elements within a dictionary based on only a portion of the key. We'll use keysDo to implement matchKeys. To add it to your system, select the Dictionary class from the browser, enter the code, and accept. /* Return a set of all keys that begin with a String */ Def matchKeys(self, aStr | aSet) { aSet := new(Set, 10); aStr := asString(aStr); keysDo(self, {using(aKey) if subString(asString(aKey), 0, size(aStr)) = aStr add(aSet, aKey) endif; }); ^aSet } This method will work with any dictionary having string or symbol keys. With matchKeys you could find every Actor constant beginning with MF_ (these are Windows(tm) constants): matchKeys(Constants, "MF_"); Extract is another powerful method available to a dictionary. Extract evaluates a block for each element of the dictionary; if the result is true (not nil), the key/element pair will be added to a new dictionary. Since the values within a dictionary can be objects of different classes, you may want to create a sub-dictionary containing only values of a specific class. We'll use extract to implement this: /* Return a new Dictionary containing only those associations where the class of value is cl */ Def matchClass(self, cl) { ^extract(self, {using(elem) class(elem) = cl }) } One use of matchClass is to find all global variables of a given class, for instance: matchClass(Actor, Int); Try developing a matchValue method that returns a set of all keys which point to a specified element. This method is very useful when developing Microsoft Windows menus; with it you can verify that there are no clashes between your menu item constants. Try writing the method again to return a dictionary instead. By incorporating dictionaries into your own applications you will often decrease development time and increase program speed. If you have developed any useful methods for the Dictionary class, or have used the class in interesting ways, please share your experiences here on the BBS. Actor is a registered trademark of The Whitewater Group, Inc. Other product names may be trademarks of their respective owners.