Nested for loop issue TCL - loops

#!/bin/sh
# This is a trial program
puts "++++++++++++++++++++++++++++++++++++++++++++++++++"
set y "0.0.0.0"
set z [split $y "."]
puts "z=$z"
lreplace $z 0 5
puts "z $z"
set v [llength $z]
puts "length of array= $v"
puts "in the loop-------->\n"
puts " "
incr v -1
puts $v
for {set ml $v } { $ml >= 0} { puts "$ml =ml"} {
for { set nl [lindex $z $ml]} { $nl >=4} { puts "$nl=nl"} {
puts $nl
after 2000
lset z $ml $nl
incr $nl
}
after 2000
incr ml -1
}
I am not able to enter the second for loop, is this a formatting issue ?
gives me some weird error. I added the sleep just to check whats happening so ignore that.

In your code your inner loop is only evaluating if nl >=4.
nl will be initialized as 0 from [lindex $z $ml]
Since you are incrementing $nl, my guess is you should change this line:
for { set nl [lindex $z $ml]} { $nl >=4} { puts "$nl=nl"} {
to this instead:
for { set nl [lindex $z $ml]} { $nl <=4} { puts "$nl=nl"} {

Was it perchance something like this you intended?
# This is a trial program
puts "++++++++++++++++++++++++++++++++++++++++++++++++++"
set y "0.0.0.0"
set z [split $y "."]
puts "\$z=$z"
set v [llength $z]
# the term 'array' means associative array in Tcl, better use 'list'
puts "length of list= $v"
puts "in the loop-------->\n\n"
incr v -1
puts "\$v=$v"
for {set ml $v} {$ml >= 0} {incr ml -1} {
for {set nl [lindex $z $ml]} {$nl <= 4} {incr nl} {
lset z $ml $nl
puts $z
}
}
Note that I've moved the incr command invocations to the third argument (the next command string, as the documentation puts it) of the for command invocations. You can put anything you want to run at the end of each iteration there, including puts commands as you did, but it's a convention and good practice to have the loop-control-changing commands (whatever they may be) there, and not much else.

Related

TCL create a list

I'm an absolute beginner and can't quite wrap my head around Tcl. I need some help with something that I think is very basic. Any help would be appreciated. I have a text file that I want to import into Tcl. I'll give you the syntax of the file and my desired way to store it:
text FILE to import into Tcl:
Singles 'pink granny fuji'
Singles2 'A B C D E'
Couples 'bread butter honey lemon cinnamon sugar'
Couples2 'A1 A2 B1 B2 C1 C2 D1 D2'
My desired format:
For lines 1 & 2:
Singles
[pink granny fuji] ( 3 single elements)
Singles2
[A B C D E] (5 single elements)
For lines 3 & 4:
Couples
[bread butter
honey lemon
cinnamon sugar] (3 x 2 array)
Couples2
[A1 A2
B1 B2
C1 C2
D1 D2] (4 x 2 array)
The import text file can in theory have any number of elements, but lines 3&4 will always be an even number of elements so that they are pairs, so I know a for each loop is needed to capture any number of elements. I know the code will also need to strip the apostrophes from the text file.
I'm just really struggling at the moment, really appreciate any help at all, thank you :)
here is solution. It works perfectly. I hope you are looking for similar solution.
set handle [open "text_import" "r"]
set content [read $handle]
regsub -all {'} $content "" content
#set content [split [read $handle] \n]
set content [split $content \n]
foreach ele $content {
puts $ele
if {[regexp -nocase -- "(\[^ \]+) +(.*)" $ele - key val]} {
puts $key
if {[regexp -nocase -- "single" $key]} {
set val1 [split $val " "]
set arr($key) $val1
} elseif {[regexp -nocase -- "couple" $key]} {
set biggerList [list]
set val2 [split $val " "]
for {set i 0} {$i < [llength $val2]} {incr i 2} {
set tempList [list [lindex $val2 $i] [lindex $val2 [expr $i + 1]]]
lappend biggerList $tempList
}
set arr($key) $biggerList
}
}
}
parray arr
~
One possible solution. Instead of loading the textfile as data, we can load it as Tcl source if we make the right definitions.
proc Singles args {
set str [string trim [join $args] ']
puts "\[$str]"
}
proc Singles2 args {
set str [string trim [join $args] ']
puts "\[$str]"
}
proc Couples args {
set list [split [string trim [join $args] ']]
foreach {a b} $list {
lappend list2 "$a $b"
}
set str [join $list2 \n]
puts "\[$str]"
}
proc Couples2 args {
set list [split [string trim [join $args] ']]
foreach {a b} $list {
lappend list2 "$a $b"
}
set str [join $list2 \n]
puts "\[$str]"
}
source textfile.txt
Documentation:
foreach,
join,
lappend,
list,
proc,
puts,
set,
source,
split,
string

Tcl: processing variables in a list

Maybe this is pretty stupid, but I really can't find a soulution. I created two variables and want to transform them into lists.
This commands are tool specific, but they work the way I want:
redirect max_transition {report_constraint -view $pargs(-scenario) -drv_violation_type {max_transition} -all_violators} -variable
redirect max_capacitance {report_constraint -view $pargs(-scenario) -drv_violation_type {max_capacitance} -all_violators} -variable
Now I want to create tcl lists out of them. I could use a loop, because the data has the same structure.
set reports {$max_transition $max_capacitance}
set report_length [llength $reports]
for {set i 0} {$i < $report_length} {incr i} {
set tns_value 0
set max_wns 0
set vios 0
set report [lindex $reports $i]
puts $report
# remove all uneccessary white spaces
set no_space [regexp -all -inline {\S+} $report]
# insert a new line for every path
set insert_lines [string map {" U_" \nU_} $no_space]
# create list out of result reports
set report_list [split $insert_lines "\n"]
if {[llength $report_list] > 1} {
for {set i 1} {$i < [llength $report_list]} {incr i} {
# get value of violation
set slack [lindex [split [lindex $report_list $i] " "] 3]
set tns_value [expr $tns_value + $slack]
if {$vios == 0} {set max_wns $slack}
incr vios
}
}
# write out values
puts "$pargs(-scenario), $report, $max_wns, $tns_value, $vios"
}
But this does not work out. The loop just puts out the variable's names (because of "puts $report") but not its content.
If I do it without a loop (so for each variable the same code consecutively), I get the lists I want.
So how can I process these variables as a whole in a loop?
The problem lies in the below loop variable i, it is overriding variable value of outer-loop. Try changing inner-loop variable to j.
for {set i 1} {$i < [llength $report_list]} {incr i} {
# get value of violation
set slack [lindex [split [lindex $report_list $i] " "] 3]
set tns_value [expr $tns_value + $slack]
if {$vios == 0} {set max_wns $slack}
incr vios
}
It's hard to write an answer for this since so much is unknown. To begin with, you should probably change to assignment by list and a foreach loop like this:
set reports [list $max_transition $max_capacitance]
foreach report $reports {
Since you don't really need to use a for loop here, it makes sense to simplify it. Please comment and I will iteratively improve the answer if I can.

How to return the value from the proc using TCL

I have a sample proc
proc exam {return_value} {
set input "This is my world"
regexp {(This) (is) (my) (world)} $input all a b c d
set x "$a $b $c $d"
return x }
After the above proc execution i will get all a b c d value in single list, so if i want only b value from the above proc, now am doing [lindex [exam] 1].
I am looking for other way to get output in different manner instead of using lindex or returun_value(b) can give the my expected output
The usual way of returning multiple values is as a list. This can be used with lassign at the call site so that the list is broken into multiple variables immediately.
proc exam {args} {
set input "This is my world"
regexp {(This) (is) (my) (world)} $input all a b c d
set x "$a $b $c $d"
return $x
}
lassign [exam ...] p d q bach
You can also return a dictionary. In that case, dict with is a convenient way to unpack:
proc exam {args} {
set input "This is my world"
regexp {(This) (is) (my) (world)} $input all a b c d
return [dict create a $a b $b c $c d $d]
}
set result [exam ...]
dict with result {}
# Now just use $a, $b, $c and $d
Finally, you can also use upvar inside exam to bring the caller's variables into scope, though there it's usually wisest to only do that with variables that the caller gives you the name of.
proc exam {return_var} {
upvar 1 $return_var var
set input "This is my world"
regexp {(This) (is) (my) (world)} $input all a b c d
set var "$a $b $c $d"
return
}
exam myResults
puts "the third element is now [lindex $myResults 2]"
You can use dict and choose such key-value mapping that will make your intent clear:
return [dict create width 10 height 200 depth 8]
I think there are no ways in Tcl to return multiple values other than compound data structures or yield from a coroutine.

Iterate while loop results to list in tcl

How to iterate while loop results to a list in Tcl?
set i 0
while { $i < ($num +1) } {
set list $i
incr i
}
create a list by using above while loop.
You've almost got it. You just need to use lappend inside the loop (and for is more idiomatic than while for this sort of thing). Note that where you put newlines is important; you can't put them between a while condition and the body of the loop (without putting a backslash in, which is ugly).
for {set i 0} {$i < ($num + 1)} {incr i} {
lappend list $i
}
You could write the following equivalent code, but the above is clearer:
set i 0
while {$i < ($num + 1)} {
lappend list $i
incr i
}
Finally, I'd actually use this:
for {set i 0} {$i <= $num} {incr i} {
lappend list $i
}
It emphasises that $num is the limit more clearly, with <= marking it as an inclusive limit. I'd do the same in many other languages too.
$i takes a value that is always going to be a number as the code is written. This value is then assigned to list. list should then be printed or stored... exactly what you want to do with list is not clear because as the code is written, it is simply overwritten on each iteration of the loop.
You also have a problem with your loop condition possibly. Unless you are looking for a list of numbers generated by the increment of i the code should be:
set i 0
while { i < ($num + 1)} {
set list $i
[here would be where you print/store list]
incr i
}
Does this help?

Retrieving modelsim signals into tcl

How can I retrieve a Modelsim signal value in this form x y into tcl so I can process x and y individually?
Currently I have this line in tcl to trace a signal value
when {/currentstate/comp_occupy} {set comp [exa
{/currentstate/comp_occupy}]}
This signal is a 2D array in Modelsim which is shown like x y in the widget.
This snippet should trace that variable
trace variable comp w grid_monitor
proc grid_monitor {name arrayindex op} {
global comp flag_ttt cells
if {$flag_ttt == 1} {
puts $comp
puts [llength $comp]
}
}
What I get out of this proc is like this {x y} but I have no idea how I can separate x and y. First I thought that's a list but llength returns 1!
Any idea how I can go about doing this? Or rather, how can I turn it into a proper list?
Thanks
Since we established that the braces were literal braces, you can trim them out. Once done, you can then split to get a list:
proc grid_monitor {name arrayindex op} {
global comp flag_ttt cells
if {$flag_ttt == 1} {
set new_comp [split [string trim $comp "{}"]]
puts $new_comp
puts [llength $new_comp]
}
}
string trim will trim from $comp the characters contained within the quotes, that is { and }. split will then split the string on space to give a list.
And if you want to assign x and y to the above, you can use lindex or lassign (if you have Tcl8.5 or later):
proc grid_monitor {name arrayindex op} {
global comp flag_ttt cells
if {$flag_ttt == 1} {
set new_comp [split [string trim $comp "{}"]]
puts $new_comp
puts [llength $new_comp]
set x [lindex $new_comp 0]
set y [lindex $new_comp 1]
puts "x is $x and y is $y"
}
}
Or...
set new_comp [split [string trim $comp "{}"]]
puts $new_comp
puts [llength $new_comp]
lassign $new_comp x y
puts "x is $x and y is $y"
In Tcl 8.5 the syntax to convert a string containing a valid list is to use the new expand operator:
set comp {*}$comp
It isn't clear if current versions of Modelsim have upgraded beyond 8.4 in which you need to do the same with eval:
eval set comp $comp
This uses the interpreter to do what it does best and avoids manually massaging the string.

Resources