1. Tree with
The tree with sub-command
assigns key values to an array and
then evaluate the script. If the node index is
a tag range or node-list, those values are iterated
over.
The value returned from with is the number of iterations.
For each iteration
key values are set in the array variable. On
completion, changed values are copied back to the tree. eg:
set t [tree create]
set id [$t insert 0 -data {Name "Larry" Age 19}]
$t with s $id {
incr s(Age)
puts "S: $s(Name), $s(Age)"
}
After evaluation, Age = 20.
2. Empty Variable
If the variable name is given as an empty string,
values are written to local variables, and * and # are not set.
Note this can be dangerous
if -keys is not used as accidental overwrites can occur.
$t with {} $id {
incr Age
puts "S: $Name, $Age"
}
3. Tags and Lists
with can use tags and node-lists.
Thus all the following are valid:
$t tag add mytag 1 2 3
$t with s mytag {
parray s
}
$t with s {1 2 3} {
parray s
}
$t with s [$t children 0] {
parray s
}
$t with s [$t find -name Mark] {
parray s
}
$t with s "" {
parray s
}
4. with Options
with can take several options. These follow the array
variable name:
| Name | Description |
| -array | A key whose values are an array. |
| -break | Handle break/continue like foreach does. |
| -keys | Specify which keys are used. |
| -glob | Pattern to limit keys. |
| -noupdate | Do not copy changes back to key. |
| -unset | Unset array var at each loop entry. |
4.1 -keys list
You can specify which keys to use with -keys:
$t with s -keys {Name Age} mytag {
puts "S: $s(Name), $s(Age)"
}
Note: using -keys
inhibits creation of the * entry.
You can alternatively limit which keys to use with a -glob pattern.
4.2 -noupdate
By default with copies any updated key variables back
to the tree node. But this can be disabled with -noupdate:
$t with b -noupdate $key {
incr b(Age)
puts "AGE: $b(Age)"
}
Thus after this call, Age remains unchanged.
A second alternative is to simply unset the variable
thus avoiding the update:
$t with b $key {
incr b(Age)
append b(Last) " Jr"
puts "AGE: $b(Age)"
array unset b
}
4.3 -unset
By default, with doesn't clear the array at
the begin of each evaluation. To change this use -unset:
$t with a -unset all {
if {![info exists a(Age)]} { puts "$a(#): No Age" }
}
4.4 -array key
The -array option specifies a
single key that is to be treated as an array.
The fields of the array for that one key are then used
(instead of keys of the whole node):
set id [$t insert -data { Users "Name Bill Age 19 Sex M" X 1}
$t with s -array Users $id {
puts "S: $s(Name), $s(Age)"
incr s(Age)
}
You can also use -keys to limit the array
elements involved:
$t with s -array Users -keys {Name Age} $id {
puts "S: $s(Name), $s(Age)"
}
If a key does not exist, or its
value can not be converted to an array,
an error will occur. However, you can use find
to limit traversal to just valid arrays eg:
set rng [$t find -key Users -isarray]
$t with p -array Users $rng {
puts "S: $s(Name), $s(Age)"
}
4.5 -break
Although with supports iteration,
it is primarly a data access mechanisim.
Thus unlike foreach, any use of
break or continue in the script gets passed
up to the enclosing script body. This may
seem counter-intuitive, but facilites multiple
nested withs, eg:
foreach {ida idb} $args {
$t with a $ida {
$s with b $idb {
# The "break"/"continue" are for "foreach"
if {$a(Age)<0 || $b(Age)<0} break
if {$b(Age)>80} continue
if {$a(Age)>$b(Age)} { incr cnt }
}
}
}
However, this
can be changed by using -break, to treat
break and continue as in
a foreach statement, eg.
proc UnderAge {t keys args} {
set cnt 0
$t with b -break $keys {
if {$b(Age)<19} continue
if {$b(Age)>99} break
incr cnt
}
return $cnt
}
Without the -break option the above would
kick an error for using continue outside of a loop.
In either case, changes to the array do update back
to the tree.
Note: this option is probably of limited
use as tree also has a foreach sub-command.
5. Validation
The code body for with can be validated by using the non-object call:
proc Work {t} {
tree op with $t s mytag {
puts "S: $s(Name), $s(Age)"
set a b c
}
}
Thus running the above with wize -Wall will
warn about too many args to set.
6. Example
Here is a bigger example using most of the options:
set t [tree create]
$t insert 0 -data {Class "A 1 B 2 C 3" Blast 99}
$t insert 0 -data {Class "A 2 B 2 C 3"}
$t insert 0 -data {Class "A 3 B 2 C 3"}
$t insert 0 -data {Class "A 3 B 2 C"}
$t with p -unset {1 2 3} {
parray p
puts -----
}
puts ######################################
$t with p -unset -array Class {1 2 3} {
parray p
puts -----
incr p(A)
}
puts ######################################
$t with p -array Class -unset -keys {A D} {1 2 3} {
parray p
puts -----
}
puts [$t find -key Class -isarray]
puts [$t find -key Class -isarray -invert -notop]
puts [$t type 3 Class]
and the output:
p(#) = 1
p(*) = Class Blast
p(Blast) = 99
p(Class) = A 1 B 2 C 3
-----
p(#) = 2
p(*) = Class
p(Class) = A 2 B 2 C 3
-----
p(#) = 3
p(*) = Class
p(Class) = A 3 B 2 C 3
-----
######################################
p(#) = 1
p(*) = A B C
p(A) = 1
p(B) = 2
p(C) = 3
-----
p(#) = 2
p(*) = A B C
p(A) = 2
p(B) = 2
p(C) = 3
-----
p(#) = 3
p(*) = A B C
p(A) = 3
p(B) = 2
p(C) = 3
-----
######################################
p(A) = 2
-----
p(A) = 3
-----
p(A) = 4
-----
1 2 3
4
array
© 2008 Peter MacDonald