вторник, 30 октября 2012 г.

Traversing over paths in Tcl

If we have paths - string like directories of DOS, we can want to group them and walking on all of them:
Input:
  a/b/c
  a/y/u
  a/b/d
  a/y/i

Walking:
  a (a)
   b (a/b)
    c (a/b/c)
    d (a/b/d)
   y (a/y)
    u (a/y/u)
    i (a/y/i)
Next procedute does it, but paths are list of lists (each item is list of directories' names).
proc forpaths {boundVarNames paths body} {
# walk on paths, each is list of dirs. $body will execute on visit each dir,
# variables bound:
#   $keyName - current path
#   $levelName - current level (0...N)
#   $leafName - is leaf or not (1, 0)
    foreach {keyName levelName leafName} [lrange $boundVarNames 0 2] {}
    set group [dict create]
    foreach path $paths {
        dict set group {*}$path "@LEAF"
    }
    proc _cmp {a b} {
        if {[expr {$a > $b}]} { return 1 } \
        elseif {[expr {$a < $b}]} { return -1 } \
        else { return 0 }
    }
    proc _trackpath {track level dir} {
        if {$track eq ""} { set track {"@DUMMY"} }
        switch -- [_cmp [expr $level+1] [llength $track]] {
            1  { lappend track $dir }
            0  { lset track end $dir }
            -1 { set track [lreplace $track $level end $dir] }
        }
        return $track
    }
    set gtrack {}; # current path when visit each node
    proc _walk {d keyName levelName leafName body {_deep 2}} {
    # here $level is level in tree, not in stack
    # $_deep is level in stack
        upvar $_deep $keyName key $levelName level
        upvar gtrack gtrack
        if {$leafName ne ""} { upvar $_deep $leafName leaf }
        dict for {k v} $d {
            set level [expr {$_deep-2}]
            set gtrack [_trackpath $gtrack $level $k]
            set key $gtrack
            if {$v eq "@LEAF"} {
                set leaf 1
                uplevel $_deep $body
            } else {
            # nested
                set leaf 0
                uplevel $_deep $body
                _walk $v $keyName $levelName $leafName $body [expr {$_deep+1}]
            }
        }
    }
    _walk $group $keyName $levelName $leafName $body
}
It works like foreach:
forpaths {k level leaf} $someDirs {
    puts "key: $k level: $level isLeaf: $leaf"
}
Mandatory is only first variable - key, so it's possible to call:
forpath {k l f} ...
forpath {k l} ...
forpath k ...
So to traverse directories' names first split them with separator string into lists!

Комментариев нет:

Отправить комментарий

Thanks for your posting!