dictionary

A dictionary associates keys to values.

By default, dictionaries are persistent, retaining their values across games.

Dictionary entries

Formally, a dictionary is a collection of dictionary entries, each of which is a pair of permitted values. A permitted value is either a string, a number, position, or a set of squares. The first element in each dictionary entry is called the key of that entry. The second element in each dictionary entry is called the value of that entry. No two entries in the dictionary share a key.

A dictionary can have only a single type of key and only a single type of value, although the types of keys and value can be different.

A dictionary is defined by preceding its name, which is a variable, with the keyword dictionary.

persistence of dictionaries

A dictionary is persistent by default retaining its value across games, and so any use of such a dictionary will force CQL to be single-threaded.

local (non-persistent) dictionaries

If the first use of the dictionary is preceded by the keyword local the dictionary is local. A local dictionary is not persistent: it is cleared before each new game is read.
	local dictionary D
The example file followpath.cql illustrates the use of local dictionaries.

Only local dictionaries can have keys or values that are positions. If you want to store positions in a persistent dictionary, use the FEN of the dictionary or some other characteristic of the position.

Compatibility with CQL 6.1 and initializing dictionary types

Sometimes it is necessary to initialize a dummy element, just to let the parser know what the types of a dictionary are. If necessary, this dummy element can be deleted immediately after initialization:
	dictionary D["tmp"]="none"
	if D["foo"]=="bar"
	     D["foo"]="fum"
      
Initializing like this might be necessary when converting some code that uses dictionaries in CQL 6.1 over to CQL 6.2. CQL 6.1 dictionaries only allowed string keys and values for dictionaries, which allowed the parser to make certain type inferences without explicit initialization.

examples

Suppose D is a dictionary. The value of the entry in D with key key is D[key].
      dictionary D
      D["hello"]="goodbye"
      D["up"]="down"
      "goodbye" == D["hello"]
      "down" == D ["up"]
      D["fail"] // this filter will fail to match

If dictionary D has no entry with key key then D[_key_] will fail to match; otherwise the expression will match.

Dictionary values can be assigned using the syntax in the example above.

Dictionaries cannot currently be used as the actual argument to a function or returned as the value of a filter. Thus, whenever a dictionary is used, its actual name, as a variable, must be used.

counting elements of a dictionary

If D is the name of a dictionary, then #D is the number of entries in the dictionary:
  dictionary Dict
  #Dict==0
  Dict[a3]="knight"
  #Dict==1

iterating through a dictionary

Simple looping through the keys of a dictionary can be done using this syntax:
      key  dictionary body
where key is a variable; dictionary is the name of a dictionary; and body is any filter:
      dictionary E["1"]=="2"
      dictionary E["No"]=="yes"
      KE
       message
        ("E[K]: " E[K])

This syntax works with any allowed type:

      dictionary F[ a2 ]= 45
      F[ b1-8 ] = 4
      RF
        message ("F[R]: " F[R])

Note that the a2 is surrounded by spaces when enclosed in brackets. This is because the symbol

      [a2]
is interpreted as a piece designator representing the square a2 rather than an index into a dictionary. (Likewise, [x] is interpreted as × , so be careful when using a variable named x as an index into a dictionary.)

clearing entries in a dictionary

To delete an entry with key key in a dictionary D, use this syntax:
      unbind D[key]

This form of unbind always matches the position unless the key filter fails to match the position. To delete all entries in a dictionary D, use

      unbind D
This form of unbind always matches the current position.
    dictionary D
    D["check"]="mate"
    D["back"]="rank"
    #D==2
    unbind D["check"]
    not D["check"]
    #D==1
    unbind D
    #D==0

Examples of use of dictionary

Suppose we want to find pairs of games sharing the same initial position:
      cql(input "hhdbvi.pgn")
      dictionary InitialD
      initial
      if InitialD[fen]~~"gamenumber: (\d+)"
        comment ("Position already seen at game number: " \1)
      else
        InitialD[fen]=str("gamenumber: " gamenumber)

More sophisticated matching can also be done, like searching to see if any position in the current study occurred earlier, and so on.

Dictionaries are often useful with readfile and writefile. Typically, a file will be written encoding information about fens of games, and then fed into a dictionary with readfile.

summarizing dictionary contents at the end of processing

Note: Unfortunately, at this time there is no good way to iterate through a dictionary after the last position of the last game, which is useful if the dictionary is summarizing some computation.

One unpleasant workaround is the following, where user_filter was the body of the original CQL file:

      Success=0
      if user_filter Success=1
      if gamenumber==lastgamenumber and not position positionid+1{
         KeyD message(Key ": " D[Key])
      Success==1

If you do need to do this, please let CQL support know; we will help write the post-processing code for you application. We also want to know how many people need this feature.