Loop Calling Processes and assigning values in Tcl - tcl

I am currently using a loop to iterate values into a process (neighbors) in Tcl.
for {set i 0} {$i < $val(nn)} {incr i} {
for {set j 0} {$j < $val(nn)} {incr j} {
$ns at 0.0 "neighbors $node($i) $node($j) $i $j"
}
}
The above allows all the values I need to go into the process. However, inside the process some values are assigned (to a list) and no longer needed to be looped through. A brief snippet from the process:
} else {
puts "..... Do nothing .... $nd1 - $nd2"
if {([lsearch -exact $heads $nd1] != -1) || ([lsearch -exact $members $nd1] != -1) } {
incr $nd1
}
}
This is the end of a loop in the process. The puts is just a marker, but it checks if an item is contained in either of 2 two lists. If it is in either list, increment it, and move on the next possible value. That value needs to no longer be checked/looped through because it has already been put inside a list.
How do I prevent the value from continuing to be used? The 'process calling' loop will ALWAYS override what happens in the process, so even assigned values will continue to be used. Is there a different way to call processes in Tcl? Or at least, a different way to feed values to a process in Tcl? Or I guess, pull them out?
As a note, here is my process that I want to feed to (n1 and n2 are memory locations, nd1 nd2 are actual number identifiers)
set heads {}
set members {}
proc neighbors {n1 n2 nd1 nd2} {
global heads members bool allNodes
puts "Members --- $members"
puts "heads --- $heads"
if {([lsearch -exact $heads $nd1] == -1) && ([lsearch -exact $members $nd1] == -1) } {
lappend heads $nd1
set currentHead $n1
puts "Current Head: $currentHead $n1 $nd1"
} else {
puts "..... Do nothing .... $nd1 - $nd2"
if {$nd1 in $heads || $nd1 in $members} then return
#here I want it to go to the next nd1 value and NEVER use it again if it
#has already been processed
}
#Otherwise, do neighbor test with nd2

If the neighbors operation is symmetric (often true), you do the check of everything against everything else like this:
for {set i 0} {$i < $val(nn)} {incr i} {
for {set j $i} {$j < $val(nn)} {incr j} {
$ns at 0.0 [list neighbors $node($i) $node($j) $i $j]
}
}
with the inner loop starting at $i (or [expr {$i - 1}] if you don't want to check things against themselves) instead of zero. This ensures that $j is always not less than $i, effectively (approximately) halving the amount of work you need to do.
(Style point: it's considered good style to use [list ...] to prepare code for later execution, and not "..."; the former is more efficient, and the latter has some ugly cases when working with values that may have spaces in.)
What you can't do (at least not easily; there might be a method to do it) is use the result of the neighbors operation to stop future calls to neighbors from happening, as you've already scheduled them to occur by the time any of them can possibly express an opinion. It's probably easier in your case to keep a state variable and check against it for the option to do an early reject. That's a fundamental limitation of using delayed command invocation instead of direct: passing values back to do things like skipping future iterations is quite difficult (and downright tricky before Tcl 8.6; that has coroutine which simplifies the task a lot).

It feels like you want to do this:
proc neighbors {n1 n2 nd1 nd2} {
global heads members bool allNodes
if {$nd1 in $heads || $nd2 in $members} then return
... do the neighborly stuff ...
}
See https://tcl.tk/man/tcl8.6/TclCmd/expr.htm#M15 for the in operator.

Related

Xcelium simulator in endless loop using Tcl for/while

Could someone help me to understand why the below code hangs (i.e. stuck in an endless loop) in Xcelium simulator, and what should be the correct one?
for {set i 0} {$i < 2} {incr $i} { puts "i is $i"; }
set i 0
while {$i < 2} { puts "i is $i"; incr $1; }
The result for either for or while loop above is just an endless: i is 0
incr takes a variable name as input argument, so it should be incr i.
incr $i increments instead the variable 0. What happens if the variable does not exist depends on Tcl version. From the manual page:
Starting with the Tcl 8.5 release, the variable varName passed to incr may be unset, and in that case, it will be set to the value increment or to the default increment value of 1.

For loop increment by a non-integer in TCL

I want to implement the following C code in TCL:
Float power_pitch = 8
float offset = 7.5
float threshold = 100
for(i=power_pitch+offset ; i<= threshold ; i += power_pitch)
I want to implement above forloop in TCL. I have tried the following code in TCL:
set power_pitch 8
set offset 7.5
set threshold 100
for { set i [expr $power_pitch + $offset] } { $i <= $threshold } { incr i $power_pitch}
but when I execute above code I get the following error:
expected integer but got "15.5 "
while executing incr i $power_pitch
Could you help me to implement above forloop in TCL?
The incr command only works with integers. Otherwise, use:
set i [expr {$i + $power_pitch}]
The for command itself won't mind. (Be aware of float rounding issues; they're not Tcl-specific, but can hit with anything that isn't an integer multiple of a power of 2…)
Donal has already provided the answer to the question, I'd just like to make an observation in two points about the for command.
for is very nearly free-form
while an integral counting loop is a typical use of for, it's by no means the only option
The for command has the synopsis
for start test next body
where start, next, and body are command strings (i.e. appropriate as an argument to eval; they can be empty, contain single commands, or be full scripts) and test is a boolean expression string (i.e. appropriate as an argument to expr and evaluating to something that is or can be coerced into a boolean value).
Usually, start is used to set up for test and body, and next is supposed to bring the state incrementally closer to having test return a false value, but that's just a convention, not a requirement. The following are perfectly valid (but rather smelly) invocations:
for {set n 0 ; puts -nonewline X} {[incr n] < 5} {puts -nonewline X} {
puts -nonewline [string repeat - $n]
}
for {set f [open foo.txt] ; set n 0} {$n >= 0} {puts $line} {
set n [chan gets $f line]
}
Give for any combination of command strings and boolean expression, and it will run. It might execute its body forever or not even once, but it will run. Don't limit yourself to for {set i 0} {$i < 10} {incr i} {...} invocations, that's 1950s thinking.
Even if you just want to use it for counting loops, there are still lots of options, for instance:
for {set i 0} {$i < $limit} {incr i} {...} ;# simple increment
for {set i 0} {$i < $limit} {incr i $n} {...} ;# stepping increment/decrement
for {set i 0} {$i < $limit} {incr i $i} {...} ;# doubling increment
for {set i 0} {$i < $limit} {set i [expr {...}]} {...} ;# arbitrary change
Free your mind, the rest will follow.
Documentation: chan, expr, for, incr, open, puts, set, string

Combinations of all charcaters and all lengths with using less number of loops?

Brain Teaser: I self originated this question, but stuck completely.
I want to create all possible combination of all characters, but of all possible lengths. Suppose, [a-z] combination of 1 length, then [a-z] combination of 2 length, and so on till the maximum length achieved.
this could be very easily done by iterative looping.
Example for 3 length:
proc triples list {
foreach i $list {
foreach j $list {
foreach k $list {
puts [list $i $j $k]
}
}
}
}
But, it should solve using less loops (looping needs to be dynamic)
set chars "abcdefghijklmnopqrstuvwxyz"
set chars [split $chars ""]
set complete_length [llength $chars]
set start 0
set maximum_length 15
while {1} {
if {$start > $maximum_length} {
break
}
for {set i [expr $maximum_length-$start]} {$i >= 0} {incr i -1} {
# dump combinations
}
incr start
}
In this chunk, what algorithm or method i should apply? Any kind of suggestions/help/code will be appreciated.
Sry, this is not an answer, but hopefully some interesting discussion anyway:
The word "combinations" is often used way too generally, so it can be interpreted in many different ways. Let's say that you have a source list of 26 different elements, the english letters, and you want to pick 3 of them and combine in a 3 element destination list:
Can you always pick any letter from the source list, or do the elements disappear from it as you pick them? Either define "pick" (are the elements copied or moved during a pick), or define the set of source values (is there 1 of each of A-Z or an infinite amount of A-Z).
Does the order in the destination list matter? Is AHM considered to be the same combination as HAM? Define "combine".
If you have a list where not all elements are different, e.g. {2 10 10 64 100}, you have even more possibilities. Define your set of values.
Your first example prints permutations, not combinations. If that's what you want, the easiset way is a recursive procedure. Combinations are more complicated to generate.
EDIT:
I wrote this procedure for a Project Euler program. It picks all the elements, but maybe you can modify it to pick n. It takes a command prefix as argument, so you don't have to store all permutations.
package require Tcl 8.5.0
proc forEachPerm {list cmdPrefix} {
_forEachPerm {} $list $cmdPrefix
}
proc _forEachPerm {head list cmdPrefix} {
if {![llength $list]} {
{*}$cmdPrefix $head
} else {
for {set i 0} {$i < [llength $list]} {incr i} {
_forEachPerm [concat $head [lrange $list $i $i]] [lreplace $list $i $i] $cmdPrefix
}
}
}
# example use:
forEachPerm {a b c} {apply {{list} {puts [join $list]}}}

Expanded TCL interpreter in TCL

I have implemented many TCL extensions for a specific tool in the domain of formal methods (extensions are implemented in C but I do not want solution to rely on this fact). Thus, the users of my tool can use TCL for prototyping algorithms. Many of them are just linear list of commands (they are powerfull), e.g.:
my_read_file f
my_do_something a b c
my_do_something_else a b c
Now, I am interested in timing. It is possible to change the script to get:
puts [time [my_read_file f] 1]
puts [time [my_do_something a b c] 1]
puts [time [my_do_something_else a b c] 1]
Instead of this I want to define procedure xsource that executes a TCL script and get/write timing for all my commands. Some kind of a profiler. I wrote a naive implementation where the main idea is as follows:
set f [open [lindex $argv 0] r]
set inputLine ""
while {[gets $f line] >= 0} {
set d [expr [string length $line] - 1]
if { $d >= 0 } {
if { [string index $line 0] != "#" } {
if {[string index $line $d] == "\\"} {
set inputLine "$inputLine [string trimright [string range $line 0 [expr $d - 1]]]"
} else {
set inputLine "$inputLine $line"
set inputLine [string trimleft $inputLine]
puts $inputLine
puts [time {eval $inputLine} 1]
}
set inputLine ""
}
}
}
It works for linear list of commands and even allows comments and commands over multiple lines. But it fails if the user uses if statements, loops, and definition of procedures. Can you propose a better approach? It must be pure TCL script with as few extensions as possible.
One way of doing what you're asking for is to use execution traces. Here's a script that can do just that:
package require Tcl 8.5
# The machinery for tracking command execution times; prints the time taken
# upon termination of the command. More info is available too (e.g., did the
# command have an exception) but isn't printed here.
variable timerStack {}
proc timerEnter {cmd op} {
variable timerStack
lappend timerStack [clock microseconds]
}
proc timerLeave {cmd code result op} {
variable timerStack
set now [clock microseconds]
set then [lindex $timerStack end]
set timerStack [lrange $timerStack 0 end-1]
# Remove this length check to print everything out; could be a lot!
# Alternatively, modify the comparison to print more stack frames.
if {[llength $timerStack] < 1} {
puts "[expr {$now-$then}]: $cmd"
}
}
# Add the magic!
trace add execution source enterstep timerEnter
trace add execution source leavestep timerLeave
# And invoke the magic, magically
source [set argv [lassign $argv argv0];set argv0]
# Alternatively, if you don't want argument rewriting, just do:
# source yourScript.tcl
Then you'd call it like this (assuming you've put it in a file called timer.tcl):
tclsh8.5 timer.tcl yourScript.tcl
Be aware that this script has a considerable amount of overhead, as it inhibits many optimization strategies that are normally used. That won't matter too much for uses where you're doing the real meat in your own C code, but when it's lots of loops in Tcl then you'll notice a lot.
You can wrap your commands which you want to measure. And name wrappers exactly as the original ones (renaming original procs before). After that, when instrumented command is executed it actually executes the wrapper, which executes the original procedure and measure the time of execution. The example below (Tcl 8.5).
proc instrument {procs} {
set skip_procs {proc rename instrument puts time subst uplevel return}
foreach p $procs {
if {$p ni $skip_procs} {
uplevel [subst -nocommands {
rename $p __$p
proc $p {args} {
puts "$p: [time {set r [__$p {*}\$args]}]"
return \$r
}
}]
}
}
}
proc my_proc {a} {
set r 1
for {set i 1} {$i <= $a} {incr i} {
set r [expr {$r * $i}]
}
return $r
}
proc my_another_proc {a b} {
set r 0
for {set i $a} {$i <= $b} {incr i} {
incr r $i
}
return $r
}
instrument [info commands my_*]
puts "100 = [my_proc 100]"
puts "200 = [my_proc 100]"
puts "100 - 200 = [my_another_proc 100 200]"
You might want to look at the command "info complete". It can tell you if what you have accumulated so far looks complete from the point of view of most common Tcl syntax markers. It will deal with command input that might be spread across multiple physical lines.

Is there shorthand in Tcl to get a sequential array of numbers?

For example, in Perl, to get a sequential array of numbers from 1 to 10, you could simply do:
#myArray = (1 .. 10);
The two periods serve as shorthand for this operations instead of making a for loop or writing the whole thing out manually. Other languages I've used have something similar also.
Does a similar shorthand exist in Tcl?
You can define the method:
proc fillArray {a b} {
eval return \[list $a [string repeat "\[incr a\] " [incr b -$a]]\]
}
And use it as:
set myArray [fillArray 1 10]
You even can beautify the call of procedure to make it look as in perl. For that just redefine unknown procedure:
rename unknown __unknown
proc unknown {args} {
if {[llength $args] == 3} {
lassign $args a op b
if {[string is integer $a] && $op == ".." && [string is integer $b]} {
return [fillArray $a $b]
}
}
return [uplevel __unknown {*}$args]
}
After that you can write just simple as:
set myArray [1 .. 10]
:)
Not quite this one, but
% package require struct::list
1.6.1
% struct::list iota 10
0 1 2 3 4 5 6 7 8 9
Also search this for the "iota" keyword to see how this can be done using a one-liner.
With the exception of expressions (which are their own little language) Tcl has no operators and is always a strictly prefix-driven language. This means that there isn't such a convenient shorthand for doing loops. On the other hand, there's nothing particularly special about Tcl's standard commands (apart from some minor efficiency details that don't matter here) so making your own is no problem:
proc .. {from to} {
if {$from >= $to} {
for {set i $from} {$i <= $to} {incr i} {lappend out $i}
} else {
for {set i $from} {$i >= $to} {incr i -1} {lappend out $i}
}
return $out
}
puts [.. 1 10]; # --> “1 2 3 4 5 6 7 8 9 10”
You can fake infix operators by using an unknown handler (as in GrAnd's answer) but that's really quite slow by comparison with the above.
No, a similar shorthand does not exist in tcl.
If you really want shorthand, you can create your own command that looks almost the same. For example:
proc : {start ignore end} {
set result []
for {set i $start} {$i <= $end} {incr i} {
lappend result $i
}
return $result
}
puts "from 1 to 10: [: 1 .. 10]"