Skip to content

Dictionariesđź”—

A dictionary (often also called “dict”) is an associative, array-like container. Similarly to vectors, it can store an arbitrary number of values. In contrast, the values are each associated with unique keys (integers or strings), and each key can refer either to a tuple or to an iconic object.

Example

create_dict (DictHandle)
* Add data.
set_dict_tuple (DictHandle, 'SingleInteger', 27)
set_dict_tuple (DictHandle, 'MixedTuple', ['The answer', 41])
set_dict_object (Image, DictHandle, 'SingleImage')
* Access by key.
get_dict_tuple (DictHandle, 'MixedTuple', Tuple)
* Update value.
RightAnswer := Tuple[1] + 1
set_dict_tuple (DictHandle, 'MixedTuple', ['The answer',RightAnswer])

Using Dictionary Init and Access Expressionsđź”—

Since HALCON 21.11, you can alternatively use a “dot notation”-like syntax instead of the “operator syntax” above. These expressions allow you to create the dictionary and assign values in one go, for example. However, the alternative syntax does not offer additional functionality.

The init and access expressions for dictionaries come in two flavors – “dynamic” or “static syntax”. Due to the broader scope of the dynamic syntax notation, we introduce it first:

Creating Dictionariesđź”—

To create a dictionary, use the dict init expression.

<DictHandle> := dict{['<key>']: <value>}

Example

DictHandle := dict{['SingleInteger']: 27, ['MixedTuple']: ['The answer', 42]}

Reading Dictionariesđź”—

To look up the value associated with a key, use the dict access expression.

<DictValue> := <DictHandle>.['<key>']

Writing Dictionariesđź”—

To assign a value to a key, use a dict access expression. If the key is not present in the dictionary yet, the corresponding key-value pair will be added.

<DictHandle>.['<key>'] := <NewDictValue>

The following code shows the example from above using dict expressions.

Example

* Add data.
DictHandle := dict{['SingleInteger']: 27, ['MixedTuple']: ['The answer',  \
              41]}
read_image (DictHandle.['SingleImage'], 'printer_chip/printer_chip_01')
* Access dict's tuple element.
RightAnswer := DictHandle.['MixedTuple'][1] + 1
* Update dict value.
DictHandle.['MixedTuple'] := ['The answer',RightAnswer]

Tip

Dict access expression can increase the runtime when handling larger tuples, because the tuple is copied internally. Therefore, instead of repeatedly accessing a tuple in a loop, it is more efficient to access the tuple outside the loop and subsequently iterate over the tuple content.

Simplifying the Code Using Static Syntaxđź”—

Often, you can further simplify the expressions compared to “dynamic syntax” and use “static syntax” instead. To create a dictionary in static syntax notation, use

<DictHandle> := dict{<key>: <value>}

Example

DictHandle := dict{SingleInteger: 27, MixedTuple: ['The answer', 42]}

The example from above now looks like this:

* Add data.
DictHandle := dict{SingleInteger: 27, MixedTuple: ['The answer', 41]}
read_image (DictHandle.SingleImage, 'printer_chip/printer_chip_01')
* Access dict's tuple element.
RightAnswer := DictHandle.MixedTuple[1] + 1
* Update dict value.
DictHandle.MixedTuple := ['The answer',RightAnswer]

Differences Between Dynamic and Static Syntaxđź”—

Although each of the programs mentioned above leads to the same result, some tasks can only be achieved using dynamic syntax: In case of static syntax, <key> is always interpreted as a string. This means that you cannot access the value of a variable. If you want to do this, use the dynamic syntax. In addition, the reserved words as described in the corresponding section cannot be used with the static syntax. For example, if you have a key named global, you will have to use the dynamic syntax.

Example

Take the following code:

ImagePath := 'printer_chip/printer_chip_01'
read_image (Image, ImagePath)
Params := dict{[ImagePath]: 1}

Here, ImagePath is a variable and used as the key in the dictionary assignment:
<key> = 'printer_chip/printer_chip_01'

We can then extend the example like this:

ImagePathPrefix := 'printer_chip/printer_chip_0'

for Id := 1 to 5 by 1
  read_image (Image, ImagePathPrefix + Id)
  Params := dict{[ImagePathPrefix + Id]: Id}
endfor

Compare this with the following code using static syntax:

ImagePathPrefix := 'printer_chip/printer_chip_0'

for Id := 1 to 5 by 1
  read_image (Image, ImagePathPrefix + Id)
  Params := dict{ImagePathPrefix + Id: Id} // error!
endfor

In this case, ImagePathPrefix is interpreted as a string and the dictionary Params would contain the syntactically wrong key 'ImagePathPrefix' + 1, while the output of read_image is not being used any further.

The following example shows another case requiring dynamic syntax:

Keys := ['a', 'b', 'c']

for i := 0 to |Keys| - 1 by 1
  Val := Params.[Keys[i]]
endfor

The same could be written with a single expression. When using multiple keys in one dict access or assignment expression, the elements to which the keys refer must all be tuples of length 1.

Keys := ['a', 'b', 'c']
Vals := Params.[Keys]

An empty list of keys yields an empty tuple.

Keys := []
* Vals will always be [] in this case.
Vals := Params.[Keys]

Assigning control values to multiple keys can be done in a single expression.

Example

* Dict.a == 3 and Dict.b == 3 and Dict.c == 3
Dict.[['a', 'b', 'c']] := 3

* Dict.a == 1 and Dict.b == 2 and Dict.c == 3
Dict.[['a', 'b', 'c']] := [1, 2, 3]

* Dict.a == [] and Dict.b == [] and Dict.c == []
Dict.[['a', 'b', 'c']] := []

* Dict.a == [2, 4] and Dict.b == [1, 'x'] and Dict.c == [3, 7]
* Note that this broadcast assignment only works for even multiples of the number of keys.
Dict.[['a', 'b', 'c']] := [2, 4, 1, 'x', 3, 7]

Nesting Dictionariesđź”—

The dict init expression allows arbitrary nesting. You can create further dictionaries within a dictionary. For example:

NestedDict := dict{Outer: dict{Inner: [1, 'two', dict{Innermost: 33}]}}

Restrictionsđź”—

Currently, the following restrictions apply in regard to dict expressions:

  • Accessing a dict element from a tuple select expression.
    X := [5, dict][1].x // doesn't work!

  • In a for loop, a value of a dictionary cannot be used as index.
    for MyDict.Index := 0 to 100 by 1 // doesn't work!

  • par_start cannot be used with a dictionary value. par_start<T1>: tuple_add(2,2,Dict.Key1) // doesn't work!

  • par_start cannot write the thread handle into a dictionary.
    par_start<Dict.ThreadID>: tuple_add(2,2,Four) // doesn't work!

Value Copy Semanticsđź”—

The data stored in the dictionary is a full owning copy in case of basic control values. In contrast, handles and iconic objects are copied as a shared reference copy. In particular, note the following:

Objects
The copy is a reference, as in copy_obj. In particular, changes made using the operators set_grayval or overpaint_region affect the object stored in the dictionary as well.
Handles
Storing any handle in the dictionary will copy the handle value, but not the resource behind the handle.
This is illustrated in the following example:

Example

create_generic_shape_model (ModelID1)
set_generic_shape_model_param (ModelID1, 'min_score', 0.42)
DictHandle := dict{sbm_model_handle: ModelID1}
ModelID2 := DictHandle.sbm_model_handle
set_generic_shape_model_param (ModelID1, 'min_score', 0.73)
get_generic_shape_model_param (ModelID2, 'min_score', MinScoreModel2)

ModelID1 and ModelID2 are both handles for the same model. As a consequence, the retrieved MinScoreModel2 is 0.73, the value set a line above and not the value set before setting the model handle as dictionary entry.