Tcl foreach loop not finding second variable - tcl

I am sure this has been asked here before but I can't seem to find any link that can help me with my particular problem.
I am using tcl scripting within vmd to do a quick analysis.
package require pbctools
set wat [atomselect top "segid HETA"]
set pbcbox [pbc get -now]
set Lz [lindex [lindex $pbcbox 0] 2]
set Qwat 0
puts [$wat get charge]
foreach i{$wat get z} j{$wat get charge} {set $Qwat [expr $Qwat + $j * ($i + 0.5 * $Lz) / $Lz]}
#puts $Qwat
When I run this script, I get the error:
Where atomselect 0 shows that it set wat fine, 100.608742 is the length of the box Lz and it is finding the charge for each atom (there are 4 atoms each with a negative charge).
However it is failing on this foreach loop where I am wanting it to iterate by location and charge at the same time. So line 1 in list 1 and line 1 in list 2. I have tried each syntax iteration I could think of but I cannot seem to get this expression to work within the foreach loop.
What am I doing incorrectly here?

The problem is that you are missing some whitespace for Tcl to understand what you want.
As a result foreach i{$wat get z} j{$wat get charge} {} creates 3 variables (assuming $wat is "atomselect0"):
"i{atomselect0", with a value of "get"
"z}", with a value of "j{atomselect0"
"get", with a value of "charge}"
As you can see, there is no variable "j".
My guess of what you want is probably something along the lines of:
foreach i [$wat get z] j [$wat get charge] {
set $Qwat [expr {$Qwat + $j * ($i + 0.5 * $Lz) / $Lz}]
}
(Except for some unusual circumstances, it's usually advisable to brace your expressions. So I made that change too, although it wasn't strictly necessary.)

Related

How to retrieve the last command's return value in TCL interpreter?

In Tcl, if I evaluate a command, and my expression failed to store the result in a variable, other than reevaluating the command, is there a way to retrieve the last command's return value in a TCL interpreter? For example, in some lisps, the variable * is bound to the result of the last evaluation, is there something like that in TCL?
The Tcl interpreter loop — which is pretty simple minded, with very few hidden features — doesn't save the last result value for you; it assumes that if you want to save that for later, you'll do so manually (or you'll repeat the command and get the result anew so you can save it then).
If you want to save a value for later, use set. You can use * as the name of the variable (it is legal) but it's annoying in practice because the $var syntax shortcut doesn't work with it and instead you'd need to do:
% set * [expr {2**2**2**2}]; # Or whatever...
65536
% puts "the value was ${*}... this time!"
the value was 65536... this time!
% puts "an alternative is to do [set *]"
an alternative is to do 65536
This will probably be a bit annoying when typing; use a name like it instead.
% set it [expr {2**2**2**2}]; # Or whatever...
65536
% puts [string length [expr {123 << $it}]]
19731
(That last number has rather a lot of digits in it, more than I expected…)
For example, in some lisps, the variable * is bound to the result of
the last evaluation, is there something like that in TCL?
It depends, but in the non-error case, catch can be used to, well, catch the result of the last successful evaluation within the enclosed script:
proc foo {} {return FOO}
proc bar {} {return BAR}
if {![catch {
if {rand() < 0.5} {foo} else {bar}
} * opts]} {
# handle result of last cmd evaluation
puts ${*}
} else {
# handle an error
puts [dict get $opts -errorinfo]
}
If you happen to know the command names in advance, command traces might be another option.

Calculation of Standard Deviation of a column from a file through Tcl script using " expr " function

I want to know the Tcl script required for calculation of standard deviation of a column from a file using "expr" function. Suppose I have a input file named "input.dat" which contains the following data:
6 7
5 6
3 8
9 2
And I want to calculate the the standard deviation of "Column 2" only using the "expr" function of Tcl, so what will be the Tcl script for doing the calculation. I am very much new in this Tcl scripting, can anybody please help me with the Tcl script ?
There's two key parts to this. The first is to get the column of data from the file, and the second is to calculate the standard deviation of that data. Since we've got easily identifiable parts, this is a great opportunity to make some procedures.
There's a few ways to write the column loader. Here's one:
proc LoadColumn {filename columnIndex} {
set f [open $filename]
set column {}
while {[gets $f line] >= 0} {
set datum [lindex [split $line] $columnIndex]
if {$datum != ""} {
lappend column $datum
}
}
close $f
return $column
}
Now, calculating the standard deviation is simple enough now that we don't need to mix I/O in with it. The formula's basically just copied straight off Wikipedia, after all:
proc StandardDeviation {data} {
set n [expr {double([llength $data])}]
set sum [tcl::mathop::+ {*}$data]
set mean [expr {$sum / $n}]
set sum2 [tcl::mathop::+ {*}[lmap x $data {expr {($x - $mean) ** 2}}]]
return [expr {sqrt($sum2 / ($n - 1))}]
}
(The only real advanced technique here is [tcl::mathop::+ {*}…] to calculate the sum of a list. You can use a foreach and expr instead, but it takes more code.)
Then we can plug the pieces together:
set file "myfile.dat"
set column [LoadColumn $file 1]; # Column indices count from zero
set sd [StandardDeviation $column]
puts "The standard deviation is $sd"
Splitting problems up is vital to making programs that you can understand.

How to pass arguments to tcl scripts when using tclsh [duplicate]

This is the code in TCL that is meant to produce factorial of a number given as parameter by the user.
if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{
for {set result 1} {$x>1}{set x [expr $x - 1]}{
set result [expr $result * $x]
}
return $result
}
set res [Factorial $f]
puts "Factorial of $f is $res"
There is a similar SO question, but it does not appear to directly address my problem. I have double-checked the code for syntax errors, but it does not compile successfully in Cygwin via tclsh producing the error:
$ tclsh ext1-1.tcl
extra characters after close-brace
while executing
"if {$argc !=1}{
puts stderr "Error! ns called with wrong number of arguments! ($argc)"
exit 1
} else
set f [lindex $argv 0]
proc Factorial {x}{..."
(file "ext1-1.tcl" line 3)
TCL Code from: NS Simulator for Beginners, Sophia-Antipolis, 2003-2004
Tcl is a little bit more sensitive about whitespace than most languages (though not as much as, say, Python). For instance, you can't add unescaped newlines except between commands as command separators. Another set of rules are that 1) every command must be written in the same manner as a proper list (where the elements are separated by whitespace) and 2) a command invocation must have exactly the number of arguments that the command definition has specified.
Since the invocation must look like a proper list, code like
... {$x>1}{incr x -1} ...
won't work: a list element that starts with an open brace must end with a matching close brace, and there can't be any text immediately following the close brace that matches the initial open brace. (This sounds more complicated than it is, really.)
The number-of-arguments requirement means that
for {set result 1} {$x>1}{incr x -1}{
set result [expr $result * $x]
}
won't work because the for command expects four arguments (start test next body) and it's only getting two, start and a mashup of the rest of other three (and actually not even that, since the mashup is illegal).
To make this work, the arguments need to be separated:
for {set result 1} {$x>1} {incr x -1} {
set result [expr {$result * $x}]
}
Putting in spaces (or tabs, if you want) makes the arguments legal and correct in number.

Cannot evaluating variable within IF and For loop using TCL

In following code in TCL I cannot seem evaluate the variable "a"
I'm evaluating x and y, in the same For Loop I have a IF statement that is checking for a range between x and y.
If valid then I'd like to perform some more calculations within the IF condition.
Every thing is fine up to the IF condition, but I cant seem to evaluate "a".
I'm trying to set "a" to the value of "y" for all the values within the range $min <= $x && $x <= $max
I would kindly request the experts to highlight the mistake.
for {set i 0} {$i < $lenght} {incr i} {
set x [expr ([lindex $cal1 $i])*$offset]
set y [expr ((cal2)/2) ]
if {$min <= $x && $x <= $max } {
puts "is Active"
set a [lindex $y $i]
puts a = $a
}
}
There is a lot that seems problematic in your code.
In the first line, you use the variable lenght. Tcl doesn't care about spelling, but if you don't have such a variable (you might possibly have a length variable instead) you will get an error.
In the invocation expr ([lindex $cal1 $i])*$offset] you have an unnecessary parenthesis but no braces around the expression (the braces aren't mandatory but should be there unless there is a very good reason to omit them). Also: "offset" usually means something you add to, not multiply with, another value. The invocation expr {[lindex $cal1 $i] * $offset}] would be better.
The variable y is used as a list argument to lindex later on, but it's created as a scalar variable. Also, your expression divides a string (or rather, an invalid bareword) with 2. Maybe you meant lappend y [expr {$cal2 / 2}]? If you use lappend, each value will be added to the end of an existing list, or as the first element of a new list if y doesn't exist. This is usually what one wants, but it means that the list y should be reset using set y [list] or set y {} before entering the loop, to get rid of elements added earlier, if any.
puts a = $a won't work, because if there are more than one argument to puts they are expected to be the flag -nonewline and/or a channel id to send the output to. Maybe you meant puts "a = $a".

expected integer but got "floating point number" error

I try to write a very simple program in TCL using list.
Below is the list
list { 1 2 3 4 5 6 1.5 7 }
Below is my code
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
incr sum [lindex $list $i]
}
puts $sum
On executing the above program I am getting the below error due to floating point value of 1.5 in the list
expected integer but got "1.5"
(reading increment)
invoked from within
"incr sum [lindex $list $i]"
I searched on internet and could not find anything relevant.
Please advise how do I handle the floating point value?
While using incr command, variable must have value that can be interpreted as a an integer. See tcl wiki.
If variable is a non-integral real number, [incr] could not be used, but [set] could:
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
set sum [expr {$sum + [lindex $list $i]}]
}
puts $sum
Omsai's answer should solve your problem, but a cleaner solution is to use foreach:
set sum 0
foreach n $list {
set sum [expr {$sum + $n}]
}
puts $sum
Summing up a list of numeric values can also be done with the ::tcl::mathop::+ command:
::tcl::mathop::+ {*}$list
This looks more complicated that it is. The + command isn't available in the regular namespace, so you need to specify where it comes from (the ::tcl::mathop namespace). The command expects to get each operand as a separate argument, so if they are in a list you need to expand that list using the {*} prefix.
foreach and the various mathop commands are documented here: foreach, mathop.
(Note: the 'Hoodiecrow' mentioned in the comments is me, I used that nick earlier.)
Tcl gives an error if you will try
incr a 1.5
you have to change the logic.
clearly you want to add all the numbers in the list. and answers are easy and many. But i will give you the shortest way:
set l { 1 2 3 4 5 6 1.5 7 }
set sum [expr [join $l +]]
NO LOOPING REQUIRED.