Recent Changes - Search:

TreeWith

(:toc:)

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.

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"
  }

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
  }

with Options

with can take several options. These follow the array variable name:

NameDescription
-arrayA key whose values are an array.
-breakHandle break/continue like foreach does.
-keysSpecify which keys are used.
-globPattern to limit keys.
-noupdateDo not copy changes back to key.
-unsetUnset array var at each loop entry.

-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.

-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
     }

-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" }
  }

-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)"
  }

-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.

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.

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
Edit - History - Print - Recent Changes - Search
Page last modified on August 28, 2010, at 09:13 AM