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.
Reading Dictionariesđź”—
To look up the value associated with a key, use the dict access expression.
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.
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
Example
The example from above now looks like this:
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:
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.
An empty list of keys yields an empty tuple.
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 operatorsset_grayval
oroverpaint_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
andModelID2
are both handles for the same model. As a consequence, the retrievedMinScoreModel2
is 0.73, the value set a line above and not the value set before setting the model handle as dictionary entry.