get var name interpolated when evaluating splatter Tcl - 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

Related

How to recall a variable within the "string map" option in 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

how i can insert item to list in tcl from user input

Hi i'm new to tcl i'm trying to insert element to list in proc from user input and return the list and invoke it in another list
i have tried this and i'm get
puts "Enter list Size"
set size [gets stdin]
set aList [fillTheList $size]
proc fillTheList {arg1 } {
set lList {}
for {set i 0} {$i <= $arg1} {incr i} {
set value [gets stdin]
linsert $lList $i int(value)]
puts "[lindex $lList $i]"
}
return $lList
}
and i'm getting this error in cmd
invalid command name "fillTheList"
while executing
"fillTheList $size"
invoked from within
"set aList [fillTheList $size]"
(file "ascending.tcl" line 5)
Try
proc fillTheList {arg1 } {
set lList {}
for {set i 0} {$i < $arg1} {incr i} {
puts -nonewline "Enter value "
set value [gets stdin]
lappend lList $value
puts [lindex $lList $i]
}
return $lList
}
puts -nonewline "Enter list Size "
set size [gets stdin]
set aList [fillTheList $size]
A couple of notes:
If you set the condition in the for invocation to $i <= $arg1 it will ask for one more list item than you wanted, since i starts from 0.
Instead of lappend, lset lList $i $value could be used. It used to only be able to change elements already in the list, but nowadays it can change the element after the last one in the list, extending the list by one.
lList is a really bad variable name, because it is easy to mix up with names like IList.
Tcl is barely typed at all. You type strings from the keyboard, those strings are entered in the list. If those strings are valid integers they can be used like integers. You don't need, and you can't, convert them.
Documentation:
< (operator),
for,
gets,
incr,
lappend,
lindex,
lset,
proc,
puts,
return,
set

Replace several lines of commands with a single variable in tcl

I know I have been asking a lot of questions but I'm still learning tcl and I haven't found anything that similar to this issue anywhere so far. Is it at all possible to replace a set f commands in tcl with one variable function0 for example?
I want to be able to replace the following code;
set f [listFromFile $path1]
set f [lsort -unique $f]
set f [lsearch -all -inline $f "test_*"]
set f [regsub -all {,} $f "" ]
set len [llength $f]
set cnt 0
with a variable function0 because this same code appears numerous times within the script. I should mention it appears both in a proc and not in a proc
The above code relates to similar script as
while {$cnt < $len} {
puts [lindex $f $cnt]
incr cnt
after 25; #not needed, but for viewing purposes
}
Variables are for storing values. To hide away (encapsulate) some lines of code you need a command procedure, which you define using the proc command.
You wanted to hide away the following lines
set f [listFromFile $path1]
set f [lsort -unique $f]
set f [lsearch -all -inline $f "test_*"]
set f [regsub -all {,} $f "" ]
set len [llength $f]
set cnt 0
to be able to just invoke for instance function0 $path1 and have all those calculations made in one fell swoop. Further, you wanted to use the result of calling the procedure in code like this:
while {$cnt < $len} {
puts [lindex $f $cnt]
# ...
Which means you want function0 to produce three different values, stored in cnt, len, and f. There are several ways to have a command procedure return multiple values, but the cleanest solution here is to make it return a single value; the list that you want to print. The value in len can be calculated from that list with a single command, and the initialization of cnt is better performed outside the command procedure. What you get is this:
proc function0 path {
set f [listFromFile $path]
set f [lsort -unique $f]
set f [lsearch -all -inline $f test_*]
set f [regsub -all , $f {}]
return $f
}
which you can use like this:
set f [function0 $path1]
set len [llength $f]
set cnt 0
while {$cnt < $len} {
puts [lindex $f $cnt]
incr cnt
after 25; #not needed, but for viewing purposes
}
or like this:
set f [function0 $path1]
set len [llength $f]
for {set cnt 0} {$cnt < $len} {incr cnt} {
puts [lindex $f $cnt]
after 25; #not needed, but for viewing purposes
}
or like this:
set f [function0 $path1]
foreach item $f {
puts $item
after 25; #not needed, but for viewing purposes
}
This is why I didn't bother to create a procedure returning three values: you only really needed one.
glenn jackman makes a very good point (or two points, actually) in another answer about the use of regsub. For completeness, I will repeat it here.
Tcl is a bit confusing because it usually allows string operations (like string substitution) on data structures that aren't formally strings. This makes the language very powerful and expressive, but also means that newbies do not always get the kick in the shins that a regular type system would give them.
In this case you created a list structure inside listFromFile by reading a string from a file and then using split on it. From that point on it's a list and you should only perform list operations on it. If you wanted to take out all commas in your data you should either perform that operation on each item in the list, or else perform the operation inside listFromFile, before splitting the text.
String operations on lists will work, but sometimes the result will be garbled, so mixing them should be avoided. The other good point was that in this case string map is preferable to regsub, if nothing else it makes the code a bit clearer.
Documentation: for, foreach, lindex, llength, lsearch, lsort, proc, puts, regsub, set, split, string, while
(more of a comment than an answer, but I want the formatting)
One thing to be aware of: $f holds a list, then you use the string command regsub on it, then you treat the result of regsub as a list again.
Use list commands with list values. I'd replace the regsub command with
set f [lmap elem $f {string map {"," ""} $elem} ]
for Tcl version 8.5 or earlier, you could do this:
for {set i 0} {$i < [llength $f]} {incr i} {
lset f $i [string map {, ""} [lindex $f $i]]
}

Remove duplicate elements from list in Tcl

How to remove duplicate element from Tcl list say:
list is like [this,that,when,what,when,how]
I have Googled and have found lsort unique but same is not working for me. I want to remove when from list.
The following works for me
set myList [list this that when what when how]
lsort -unique $myList
this returns
how that this what when
which you could store in a new list
set uniqueList [lsort -unique $myList]
You could also use an dictionary, where the keys must be unique:
set l {this that when what when how}
foreach element $l {dict set tmp $element 1}
set unique [dict keys $tmp]
puts $unique
this that when what how
That will preserve the order of the elements.
glenn jackman's answer work perfectly on Tcl 8.6 and above.
For Tcl 8.4 and below (No dict command). You can use:
proc list_unique {list} {
array set included_arr [list]
set unique_list [list]
foreach item $list {
if { ![info exists included_arr($item)] } {
set included_arr($item) ""
lappend unique_list $item
}
}
unset included_arr
return $unique_list
}
set list [list this that when what when how]
set unique [list_unique $list]
This will also preserve the order of the elements
and this is the result:
this that when what how
Another way, if do not wanna use native lsort function.This is what the interviewer asks :)
`set a "this that when what when how"
for {set i 0} {$i < [llength $a]} {incr i} {
set indices [lsearch -all $a [lindex $a $i]]
foreach index $indices {
if {$index != $i} {
set a [lreplace $a $index $index]
}
}
}
`

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.