How to recall a variable within the "string map" option in tcl - tcl

I want to sequentially update a series of file, from a tmp file distTmp.RST to sequentially dist1.RST, dist2.RST, etc..
For me, the fileutil package in vmd text interface is not working as follows:
My tcl code (add.tcl):
package require fileutil
set F 20.5
set Ff ""
for {set f 0} {$f < 70} {incr f} {
set F [expr {$F+1}]
lappend Ff $F
}
puts $Ff
for {set f 0} {$f < 70} {incr f} {
set M [lindex $Ff $f]
set N [expr {$f+1}]
package require fileutil
::fileutil::updateInPlace distTmp.RST {string map {WWW $M}}
::fileutil::cat dist$N.RST
}
========
The error occurring is
vmd > source add.tcl
can't find package fileutil
vmd >
========
Moreover, when I do not use "fileutil" package, my script is as follows:
set F 20.5
set Ff ""
for {set f 0} {$f < 70} {incr f} {
set F [expr {$F+1}]
lappend Ff $F
}
puts $Ff
for {set f 0} {$f < 70} {incr f} {
set M [lindex $Ff $f]
set N [expr {$f+1}]
set dat [open "distTmp.RST" r]
set out [open "dist$N.RST" w]
while {[gets $dat line] >= 0} {
set newline [string map {WWW $$M} $line]
puts $out $newline
}
}
======
But, there is a problem in recalling the variable $M within a string, and my required output files are as follows:
(base) [Prathit#master]~/APP/OnlyAPP/AlphaFold2/770_res/Charmm-Gui_Dimer-units/E2-E2_3222212666/charmm-gui-3222212666/amber/RSTfiles_Equil>head -n +4 dist1.RST dist2.RST
=> dist1.RST <==
&rst iat = -1, -1, r2 = $$M, r2a = $$M,
==> dist2.RST <==
&rst iat = -1, -1, r2 = $$M, r2a = $$M,
==========
In the above, $$M should be sequentially 21.5, 22.5, and so on....
Kindly let me know of a possible solution.

The fileutil package is part of tcllib. Check that your variable auto_path includes a path where Tcl can find tcllib and fileutil.
Your list for string map is in curly braces, so it's using a literal dollar sign for $M instead of the value of the variable named M.
Change curlies to double quotes or use the list command, as answered in a comment already.
$$M is usually not okay in Tcl. Are you trying to do double interpolation? If so, I recommend using set with one argument, to retrieve a value instead of to set a value. You can use $$ in a subst command, but that's not my preference.
set name John
set var_name name
puts $$var_name --> $name
puts [set $var_name] --> John
puts [set [set var_name]] --> John
puts [subst $$var_name] --> John

Related

How to sum coordinates in TCL

I have a data file (*.dat) containing x, y, z coordinates. As following:
{26.3612117767334 40.19668960571289 54.13957977294922}
{27.351043701171875 40.57518768310547 54.05387496948242}
{29.48208999633789 42.08218765258789 56.42238235473633}
For this file I need to do a math operation as follow:
Xi + (Xf-Xi/4) ; Yi + (Yf-Yi/4) ; Zi + (Zf-Zi/4)
where "i" is the initial position and "f" the final, meaning that Xi,Yi,Zi are the data on the first line and Xf,Yf,Zf the data on the second.
I need to do these calculation for all the lines in a loop and then stored in a separate file, but I do not have idea how to do it in TCL. Thanks in advance for your help.
Since the contents of your file can be treated as a bunch of tcl lists, one per line (so basically a list of lists), parsing it is dead simple.
Something like:
set f [open file.dat]
set coords [read -nonewline $f]
close $f
for {set i 0} {$i < [llength $coords] - 1} {incr i} {
lassign [lindex $coords $i] xi yi zi
lassign [lindex $coords $i+1] xf yf zf
set xn [expr {$xi + ($xf - $xi/4.0)}]
set yn [expr {$yi + ($yf - $yi/4.0)}]
set zn [expr {$zi + ($zf - $zi/4.0)}]
puts "{$xn $yn $zn}"
}
This skips treating the last line as an initial set of coordinates because there is no next set for it.
This is a good opportunity to write a mathfunc:
proc tcl::mathfunc::f {ai af} {
expr {$ai * 0.75 + $af}
}
proc transform {file} {
set fh [open $file]
# read the first line, aka the initial "previous line"
gets $fh line
scan $line {{%f %f %f}} xi yi zi
# process the rest of the file
while {[gets $fh line] != -1} {
scan $line {{%f %f %f}} xf yf zf
puts "{[expr {f($xi, $xf)}] [expr {f($yi, $yf)}] [expr {f($zi, $zf)}]}"
lassign [list $xf $yf $zf] xi yi zi
}
close $fh
}
transform file.dat
outputs
{47.121952533721924 70.72270488739014 94.65855979919434]}
{49.9953727722168 72.51357841491699 96.96278858184814]}
I present an alternate method that uses lrange to pick the overlapping ranges of sublists that participate (so we can then process them element-wise) and then lmap to apply the same transformation expression to each coordinate axis.
# Same read-in code as Shawn's answer; it's the easiest way
set f [open file.dat]
set coords [read -nonewline $f]
close $f
foreach Ci [lrange $coords 0 end-1] Cf [lrange $coords 1 end] {
# I often like to put expressions on their own line for clarity
puts [list [lmap _i $Ci _f $Cf {expr {
$_i + ($_f - $_i/4.0)
}}]]
}
(The wrapping list call in there puts braces around the result of lmap.)

How to compare two lines in different files and output the same position in the other line in TCL?

I have two files and I want the output like below. Please help by providing me with a TCL script.
File1:
Name1: F * F F F
Name2: F F *
Name3: F F F F
File2:
Name1: AA, BB, CC, DD, EE,
Name2: AA, BB, CC,
Name3: AA, BB, CC, DD,
Output1:
Name1
AA - FAIL
BB - *
CC - FAIL
<cont>
Name2
AA - FAIL
BB - FAIL
CC - *
<cont>
Output2:
Name1
FAIL - AA CC DD EE
* - BB
Name2
FAIL - AA BB
* - CC
Name3
FAIL - AA BB CC DD
Try this following tested on tclsh8.5
set fd1 [open "input_file_1.txt" r]
set fd2 [open "input_file_2.txt" r]
set opfd [open "output_file.txt" w]
while {[gets $fd1 line] > 0 && [gets $fd2 line2] > 0} {
set line1 [split $line ":"]
set line2 [split $line2 ":"]
puts $opfd [lindex $line1 0]
set last_part_1 [string trim [lindex $line1 1] " "]
set last_part_2 [string trim [lindex $line2 1] " "]
set space_split [split $last_part_1 " "]
set comma_split [split $last_part_2 ","]
for {set i 0} {$i < [llength $space_split]} {incr i} {
puts $opfd "[string trim [lindex $comma_split $i] " "] = [string trim [lindex $space_split $i] " "]"
}
}
close $fd1
close $fd2
close $opfd
There will be file named as output_file.txt created inside current directory which contains your output.
Another way to do it:
package require fileutil
proc getInput filename {
set contents [string trim [::fileutil::cat $filename]]
set rows [split $contents \n]
concat {*}[lmap item $rows {
split $item :
}]
}
set d1 [string map {F Fail} [getInput file1.txt]]
set d2 [string map {, {}} [getInput file2.txt]]
dict for {key values} $d1 {
puts $key
foreach v1 $values v2 [dict get $d2 $key] {
puts " $v2 - $v1"
}
}
This works by recognizing the dictionary-like structure of the data files. If every piece of data is a word without spaces, this version of getInput will coerce the contents of each file to a usable dict. From there, it's just a matter of replacing the F strings with Fail strings and removing the commas, and then doing dictionary iteration over either one of the dicts and pulling in the corresponding values from the other one.
If the values in the second file may contain spaces, getInput should look like this:
proc getInput filename {
set contents [string trim [::fileutil::cat $filename]]
set rows [split $contents \n]
set res {}
foreach item $rows {
lassign [split $item :] key values
if {[string match *,* $values]} {
set values [split [string trimright $values {, }] ,]
}
lappend res $key $values
}
return $res
}
Documentation: concat, dict, foreach, if, lassign, lmap, lmap replacement, package, proc, puts, return, set, split, string

get var name interpolated when evaluating splatter Tcl

This is a rather simple one to ask. Just to clarify, I am working Tcl 8.5.
I want to get variable interpolation done:
>set p "puts me"
>puts $p
puts me
>{*}$p
me
So this works fine. Now I want a string that says puts $varname to work, and that fails.
>set k {puts $p}
>{*}$k
$p
I want Tcl to output the value of var p. Using "evil eval" gets it done:
> eval $k
puts me
How can I get the eval behaviour w/o using eval?
Thanks.
Multi-word values
Need to subst each item in the list otherwise multi-word values would become parameters.
set p {What is Awesome?}
set k {puts $p}
set len [llength $k]
# Must subst each item in the list in order
# to execute k properly.
for {set i 0} {$i < $len} {incr i} {
lset k $i [subst [lindex $k $i]]
}
{*}$k
Output:
./puts.tcl
What is Awesome?
Single Word Values
Use subst to do variable substitution.
puts.tcl
#!/usr/bin/tclsh
set p Awesome
set k {puts $p}
{*}[subst $k]
output:
./puts.tcl
Awesome
You could do this with the proc and uplevel commands.
set me {yo yo}
set p {puts $me}
proc doit {} "uplevel 1 {$p}"
doit

How to read number count of words?

How to read number count of words?
Lines has this format:
vertices_count
X, Y
X, Y
X, Y
(X, Y pair can be in the same line)
for example:
3
12.5, 56.8
12.5, 56.8
12.5, 56.8
I would like to read vertices_count number of words(escaping comma):
So for above example reading words should be:
12.5 56.8 12.5 56.8 12.5 56.8
set fh [open f r]
gets $fh num
read $fh data
close $fh
set number_re {-?\d+(?:\.\d*)?|-?\d*\.\d+}
set vertices {}
foreach {_ x y} [regexp -inline -all "($number_re),\\s*($number_re)" $data] {
lappend vertices $x $y
if {[llength $vertices] == $num * 2} break
}
puts $vertices
# => 12.5 56.8 12.5 56.8 12.5 56.8
while {[llength $vertices] < $num * 2} {
gets $fh line
foreach {_ x y} [regexp -inline -all "($number_re),\\s*($number_re)" $line] {
lappend vertices $x $y
if {[llength $vertices] == $num * 2} break
}
}
close $fh
I'm still not clear exactly what you are after. Here is some code to read data from a named file. Judging from your other question, you can have several sets of data in your input stream and this code returns them all as a list. Each element of the list is one set of coordinates
# Read the input from file
set fil [open filename.file]
set input [read $fil]
close $fil
set data [list]; # No output so for
set seekCount yes; # Next token is a vertex count
foreach token [string map {, " "} $input] {
# Convert commas to spaces
if {$seekCount} {
set nCoords [expr $token * 2];
# Save number of coordinates
set datum [list]; # Clean out vertex buffer
} else {
lappend datum $token; # Save coordinate
incr nCoords -1
if {$nCoords <= 0} {
# That was the last coordinate
lappend data $datum; # Append the list of coordinates
set seekCount yes; # and look for anopther count
}
}
}
This is a very quick-and-dirty solution, which makes no attempt to handle errors. One thing, however that it will cope with is variable amounds of whitespace and missing whitespace after the commas.
Good luck, I hope this helps.
This procedure first reads a count line, then reads that number of lines and puts as a list into $varName. It returns the number of elements in $varName, or -1 if EOF occured before a count was read.
proc getNLines {stream varName} {
upvar 1 $varName lines
set lines {}
if {[gets $stream n] < 0} {
return -1
}
while {$n > 0} {
if {[gets $stream line] < 0} {
error "bad data format"
}
lappend lines $line
incr n -1
}
return [llength $lines]
}
while {[getNLines stdin lines] >= 0} {
# ...
}

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.