|
| 1 | +package require Tcl 8.6 |
| 2 | +package require struct ;# for struct::graph |
| 3 | + |
| 4 | +proc usage {} { |
| 5 | + puts stderr {Usage: please provide a tree in an adjacency matrix form ("0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0") together with a list of vertex values ("1, 3, 5, 2, 4") and the integer to find ("4")} |
| 6 | + exit 1 |
| 7 | +} |
| 8 | + |
| 9 | +proc isInteger {x} { return [string is integer -strict $x] } |
| 10 | + |
| 11 | +proc parseList {s} { |
| 12 | + set s [string trim $s] |
| 13 | + if {$s eq ""} { usage } |
| 14 | + |
| 15 | + lmap t [split $s ","] { |
| 16 | + set t [string trim $t] |
| 17 | + if {![isInteger $t]} { usage } |
| 18 | + set t |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +proc createGraph {matrix vertices} { |
| 23 | + set n [llength $vertices] |
| 24 | + if {[llength $matrix] != [expr {$n * $n}]} { usage } |
| 25 | + |
| 26 | + set g [struct::graph] |
| 27 | + foreach v $vertices { $g node insert $v } |
| 28 | + |
| 29 | + for {set i 0} {$i < $n} {incr i} { |
| 30 | + for {set j 0} {$j < $n} {incr j} { |
| 31 | + if {[lindex $matrix [expr {$i*$n + $j}]] != 0} { |
| 32 | + set a [lindex $vertices $i] |
| 33 | + set b [lindex $vertices $j] |
| 34 | + $g arc insert $a $b |
| 35 | + } |
| 36 | + } |
| 37 | + } |
| 38 | + return $g |
| 39 | +} |
| 40 | + |
| 41 | +proc dfsCommand {target visitedRef foundRef args} { |
| 42 | + upvar $visitedRef visited |
| 43 | + upvar $foundRef found |
| 44 | + |
| 45 | + set event [lindex $args 0] |
| 46 | + set graph [lindex $args 1] |
| 47 | + set node [lindex $args 2] |
| 48 | + |
| 49 | + # We're only interested in what happens once we enter a node, not when |
| 50 | + # we leave it. |
| 51 | + if {$event ne "enter"} { return } |
| 52 | + |
| 53 | + if {[lsearch -exact $visited $node] >= 0} { return } |
| 54 | + |
| 55 | + lappend visited $node |
| 56 | + if {$node eq $target} { set found 1 } |
| 57 | +} |
| 58 | + |
| 59 | +proc dfs {graph start target} { |
| 60 | + set found 0 |
| 61 | + set visited {} |
| 62 | + |
| 63 | + # Tcllib has DFS directly in struct::graph. However, to use it for this |
| 64 | + # I need a callback to tell me whether the target was found. It is quite |
| 65 | + # flexible, in typical Tcl fashion, but I don't need it to do very much. |
| 66 | + $graph walk $start -type dfs -dir forward \ |
| 67 | + -command [list dfsCommand $target visited found] |
| 68 | + |
| 69 | + return $found |
| 70 | +} |
| 71 | + |
| 72 | +if {$argc != 3} { usage } |
| 73 | + |
| 74 | +set adjMatrix [parseList [lindex $argv 0]] |
| 75 | +set vertices [parseList [lindex $argv 1]] |
| 76 | +set target [lindex $argv 2] |
| 77 | + |
| 78 | +if {![isInteger $target]} { usage } |
| 79 | + |
| 80 | +set graph [createGraph $adjMatrix $vertices] |
| 81 | +set root [lindex $vertices 0] |
| 82 | + |
| 83 | +puts [expr {[dfs $graph $root $target] ? "true" : "false"}] |
| 84 | + |
0 commit comments