tcl data formats confusion - foreach

The motivation behind this question is learning...
I have done some TCL programming and I do have a working code, but I do not understand why some syntax is working and another not.
Problem description:
I'm writing a code that needs to set the voltages for power and ground nets.
After I enter the VDD nets as a string,
set POWER_NETS "vdd=1.1 vdda=1.2 vref=1.1"
then a simple TCL code should construct and execute the following commands:
set_vdd vdd 1.1
set_vdd vdda 1.2
set_vdd vref 1.1
That is all. It seems trivial, but I have had some difficulty implementing it. I still do not understand why sometimes my code does not work, although it seems correct to me.
Working solution: The only working solution that I came up with is following:
foreach ITEM [split $POWER_NETS] {
set NET [lindex [split $ITEM =] 0]
set VOLTAGE [lindex [split $ITEM =] 1]
set_vdd $NET $VOLTAGE
}
That's a lot of code for such a simple operation. When written in a more condensed form it still works:
foreach ITEM [split $POWER_NETS] {
set_vdd [lindex [split $ITEM =] 0] [lindex [split $ITEM =] 1]
}
But I still have a feeling that there is room for improvement here.
Learning objective: I would like to settle with this solution as optimal code (it is both short and comprehensive)
foreach ITEM [split $POWER_NETS] {
set_vdd [split $ITEM =]
}
Unfortunately this does not work. The tool that interprets the set_vdd command complains that
no value given for parameter "voltage" (use -help for full usage) :
I do not understand why this does not work. What is the difference?
When I replace set_vdd with puts, it does print all the values. So all information is there. Isn't it?
nlv12345#acc3081 CHAR_OUT_LOGS $ tclsh
% set POWER_NETS "vdd=1.1 vdda=1.2 vref=1.1"
vdd=1.1 vdda=1.2 vref=1.1
% foreach ITEM [split $POWER_NETS] {
puts [split $ITEM =]
}
vdd 1.1
vdda 1.2
vref 1.1
%

The error message is less than optimal, but the problem is that you're passing the two words out of split as a single argument to set_vdd.
If your Tcl interpreter is any currently-supported version, you can instead do this:
foreach ITEM [split $POWER_NETS] {
set_vdd {*}[split $ITEM =]
}
The {*} is a marker to say “take this list — the rest of this word — and split it into separate arguments”. While it's formally not an operator but rather a syntactic part of the Tcl language, calling it the “expansion operator” won't confuse you a lot.
Older, unsupported versions of Tcl (8.4 and before) require the use of eval to do this. You're probably OK with:
foreach ITEM [split $POWER_NETS] {
eval set_vdd [split $ITEM =]
}
but really it would be safer to do this:
foreach ITEM [split $POWER_NETS] {
eval [linsert [split $ITEM =] 0 set_vdd]
}
As you can see, the {*} simplifies things quite a bit.
However, I'd actually do this:
foreach {ITEM KEY VALUE} [regexp -all -inline {(\w+)=([\d.]+)} $POWER_NETS] {
set_vdd $KEY $VALUE
}
Like that, unexpected things in the POWER_NETS value won't cause so much trouble. OTOH, if you're not used to regular expressions then perhaps now is not the time to start with regexp…

Two other simple solutions, for inspiration and further learning:
set POWER_NETS "vdd=1.1 vdda=1.2 vref=1.1"
foreach ITEM [split $POWER_NETS] {
lassign [split $ITEM =] NET VOLTAGE
set_vdd $NET $VOLTAGE
}
This one uses lassign to assign the parts of ITEM instead of splitting them into nameless parameters.
set POWER_NETS [string map {= { }} "vdd=1.1 vdda=1.2 vref=1.1"]
foreach {NET VOLTAGE} [split $POWER_NETS] {
set_vdd $NET $VOLTAGE
}
This one replaces the equals signs with spaces and then lets foreach extract the two values pairwise.
Documentation:
foreach,
lassign,
set,
split,
string

Related

How to have the AFTER separator in Xtend just behind last item

I use Xtend to generate some textual output from my Xtext grammar. My question is really simple, but I cannot figure it out reading the documentation and examples, so please give me advice!
Let us assume, I have a list with some items. I generate for every item a line within a FOR loop using the SEPARATOR feature. At the end I want to have a final separator using AFTER. Here is an example code to demonstrate it:
val list = #["a", "b", "c"]
'''
«FOR item : list SEPARATOR "," AFTER ","»
«item»
«ENDFOR»
'''
In this way, I receive the following output:
a,
b,
c
,
Now the question: How can I have the final , separator immediately behind c and not on the new line? It's very important to me for proper formatting of my output (it's more complex than a,b,c in reality and the position of the separator matters). I want to have this:
a,
b,
c,
Experts, please help!
In your simple example, as SEPARATOR and AFTER are the same, you should skip the separator/after stuff entirely and just use
'''
«FOR item : list»
«item»,
«ENDFOR»
'''
The newlines after FOR and ENDFOR are skipped by Xtend's whitespace detection. The problem is that AFTER is applied after the loop is executed, and your loop-body contains a trailing newline that goes into the output (grayspace). You can overcome this by writing the entire loop in one line, e.g.
'''
«FOR item: list SEPARATOR ',\n' AFTER ',\n'»«item»«ENDFOR»
'''
and extract a method for when the loop's body becomes to long. For better readability, you can add any number of whitespaces within the guillemets, e.g.
'''
«FOR item: list SEPARATOR ',\n' AFTER ',\n'»«
item
»«ENDFOR»
'''
For smaller bodies, you might also consider prefer join()
'''
«list.join(',\n')»,
'''
or more general map(), reduce() whatsoever.

file name division and instantiation

set session_list [list ../../TIMING_ANALYSIS_CRPR/FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION ../../TIMING_ANALYSIS_CRPR/FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION ]
foreach j $session_list {
lappend x [regsub {^../../TIMING_ANALYSIS_CRPR/} $j ""]
}
foreach p $x {
lappend y [regsub {[^...]reports.15_03_10-22/SAVED_SESSION} $p ""]
}
foreach item $y {
lappend r "create_scenario -name $item"
}
foreach term $r {
lappend k " $term -image ./../ "
}
This is the code I created, the desired result is :
create_scenario -name FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax -image ./../FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
create_scenario -name FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin -image ./../UNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
How do I achieve this? My main problem is calling list"x" in the last foreach statement. Is there any alternative way?
You can (and I would even say should) use a single loop for that since you are doing the same thing for all elements of the list. From my understanding, you need to get the part after ../../TIMING_ANALYSIS_CRPR to get the name and the image is the name and everything after it.
So instead of removing what's not wanted, I would rather 'capture' what's wanted like this:
# Just another way to have a list
set session_list {
../../TIMING_ANALYSIS_CRPR/FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
../../TIMING_ANALYSIS_CRPR/FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
}
foreach item $session_list {
regexp -- {TIMING_ANALYSIS_CRPR/(([^/]+).+)} $item -> image name
set result "create_scenario -name $name -image ./../$image"
puts $result
}
# Prints:
# create_scenario -name FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax -image ./../FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
# create_scenario -name FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin -image ./../FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
ideone demo
In the regexp, ([^/]+) will match and capture all characters except the forward slash character after TIMING_ANALYSIS_CRPR/. Then this, plus .+ (in (([^/]+).+) gets the name, and everything else after the name since . matches all characters.
Now, the regex can be a bit confusing since we are first getting the image then the name, but that's because the outer parentheses comes first, then the name is inside it.
If you don't want to (or you find regex too hard), you might try using file commands instead:
foreach item $session_list {
# Split on the directories
set elements [file split $item]
# Get the position of TIMING_ANALYSIS_CRPR
set id [lsearch $elements "TIMING_ANALYSIS_CRPR"]
# The name is just after it so...
set name [lindex $elements $id+1]
# The image is everything after the id:
set image [file join [lrange $elements $id+1 end]]
# Or if file join somehow is not using forward slashes:
# set image [join [list [lrange $elements $id+1 end]] "/"]
set result "create_scenario -name $name -image ./../$image"
}
Note: I am using Tcl8.6 if you are using Tcl8.4 or earlier versions, some of the syntax would have to be changed

Variable range operator in PowerShell

I am trying to use a range operator to input a series of numbers for use in a PowerShell script. Here is my code:
$computers = servername + [1-9]
I would like the $computers variable to iterate the 1-9 i.e., servername1, servername 2, etc. etc. Any ideas?
1..9 | % { $computers += "servername$_`n" }
And the variable $computers will contain:
servername1
servername2
servername3
[...]
Try running only the 1..9 part on your command line and it'll be easier to see what's gonig on. You could also read up on arrays in PowerShell with Get-Help about_Arrays - look for the part about "range operator" near the beginning.
The following line of code does the same thing (and seems cleaner to me) and might be easier to understand as well.
$computers = 1..9 | foreach { "servername$_" }
Or simply 1..9 | foreach { "servername$_" } to see it on screen without saving it in a variable.

Binary expression in Lua

Does Lua contain binary expressions like PHP? For example:
$v = 5;
for ($i=0; $i < $v; $i++) {
if($v & $i) {
echo $i." ";
}
}
Echo result:
1 3 4
If so, how to use them?
Since version 5.2 Lua comes with bit32 library. bit32.band is equivalent to & operator in php. LuaJIT also has bit operations.
Edit
Well, they're not exactly equivalent, but serve the same purpose.
See Lua logical operators as described at http://www.lua.org/manual/5.1/manual.html#2.5.3.

Array of arrays confusion

I have an array of comma delimited string. I turn that into an array of arrays by splitting out the csvs. It looks okay in my loop (from the output), giving me an array of strings. However, after I add the array of strings to another array, it seems to get messed up.
Output at the end shows a character from a string, not an actual string.
foreach($n in $Names)
{
$obj = ([string]$n.value).Split(",")
"0: " + $obj[0]
"1: " + $obj[1]
#$obj.GetTYpe()
$arrInfo+= $obj
}
$arrinfo[5][0]
$arrinfo[5][1]
$a = $arrinfo[5]
$a[0]
$a[1]
Output looks something like:
0: Item 0 string
1: Item 1 string
....
I
t
I
t
What is going wrong? Why is my second array (arrInfo) seem to have just strings instead of an array of strings?
After looking around a little more, I changed the line to read: $arrinfo += , $obj and it works as expected. I don't fully understand why.

Resources