How to use entries in a listA to refer other lists with names that have prefixes same as ListA's entries - scripting

#Suppose i have a list name
set modules [list]
lappend modules moduleA
lappend modules moduleB
lappend modules moduleC
#and i have other list naming
set moduleA_sig [list]
lappend moduleA......
set moduleB_sig [list]
lappend moduleB_sig ....
set moduleC_sig [list]
lappend moduleC_sig ....
#How can I use my "modules" list entries to access though the other list, eg:
foreach mod $modules
{
llength ${mod}_sig
}

An alternative to using upvar is to use set <var_name> to retrieve the value of var_name. When <var_name> is ${mod}_sig, then you can use set to retrieve the value of the variable without the possibility of altering the value of the original variable (like upvar)
set modules {moduleA moduleB moduleC}
set moduleA_sig {1 2 3 4}
set moduleB_sig {11 22 33 44}
set moduleC_sig {111 222 333 444}
foreach mod $modules {
# Get the value of the variable named ${mod}_sig.
puts "$mod: [set ${mod}_sig]"
}

Another approach is to use an array (Or dict) to hold values instead of trying to compose variable names on the fly at runtime.
Array:
#!/usr/bin/env tclsh
set modules {moduleA moduleB moduleC}
set sig(moduleA) {1 2 3 4}
set sig(moduleB) {11 22 33 44}
set sig(moduleC) {111 222 333 444}
foreach mod $modules {
puts "$mod: $sig($mod)"
}
Or with a dict:
#!/usr/bin/env tclsh
set modules {moduleA moduleB moduleC}
dict set sig moduleA {1 2 3 4}
dict set sig moduleB {11 22 33 44}
dict set sig moduleC {111 222 333 444}
foreach mod $modules {
puts "$mod: [dict get $sig $mod]"
}

This is where you want the upvar command
to "alias" one variable to another.
set modules {moduleA moduleB moduleC}
set moduleA_sig {1 2 3 4}
set moduleB_sig {11 22 33 44}
set moduleC_sig {111 222 333 444}
foreach mod $modules {
upvar 0 ${mod}_sig this
puts [list $mod $this]
}
outputs
moduleA {1 2 3 4}
moduleB {11 22 33 44}
moduleC {111 222 333 444}
Here, use level 0 to indicate the current stack frame.
Any modifications you make to $this will be reflected in $moduleX_sig.
set x 42
upvar 0 x y
incr y
puts $x ;# => 43

Related

wants to set a variable with another variable in it using Tcl

i want to execute this code but it is not waorking , the tcl script is as follows:
set i 0
foreach pattern { tiger cat horse dog} {
set pat$i abc
puts "pat$i=${pat$i}"
set i [expr {$i + 1}]
}
the desired result i want in every loop is :
pat0=abc
pat1=abc
pat2=abc
pat3=abc
Please help me to find my mistake
You can read a variable with a computed name with the one-argument form of set:
set i 0
foreach pattern { tiger cat horse dog} {
set pat$i abc
puts "pat$i=[set pat$i]"
set i [expr {$i + 1}]
}
The $... syntax can be considered shorthand for that (except with different, more limited parsing rules that make it more convenient to write).
Alternatively, you can use upvar 0 to make a variable alias with a simple name:
set i 0
foreach pattern { tiger cat horse dog} {
upvar 0 pat$i p
set p abc
puts "pat$i=$p"
set i [expr {$i + 1}]
}
That is much more favoured in a procedure.
HOWEVER, are you sure you need the variable to be called that? Can you use an associative array instead? That's often just as convenient.
set i 0
foreach pattern { tiger cat horse dog} {
set pat($i) abc
puts "pat$i=$pat($i)"
incr i; # The incr command is *right there*
}

pass variable from lower proc to upper proc in Tcl

I have the following code and it prints the same value of variable c in the inner loop.
global c
proc one { a c } {
for {set i $a} {$i < 10} {incr i} {
two $c
}
}
proc two { c } {
incr c
puts "Value of c is $c"
}
When I run it with the following inputs:
two 0 3
it prints 10 times "Value of c is 4" instead of keeping increasing the value of c inside the loop from 4 to 13.
The problem is the value of c from proc two is not passing up again to the for loop and it takes the same value from proc one parser c as 3. How can I get the desired output?
It sounds like you want to pass the variable c from proc one to proc two by reference so that changes to the value of the variable are reflected back in the caller.
One way to accomplish this in tcl is to pass the name of the variable, and use the upvar command, as follows:
proc one { a c } {
for {set i $a} {$i < 10} {incr i} {
two c ;# <= note: passing 'c' by name, not by value
}
}
proc two { c_name } {
upvar $c_name c ;# <= note: c in this proc is using callers variable
incr c
puts "Value of c is $c"
}
one 0 3
The above produces the following output:
Value of c is 4
Value of c is 5
Value of c is 6
Value of c is 7
Value of c is 8
Value of c is 9
Value of c is 10
Value of c is 11
Value of c is 12
Value of c is 13
I am aware that the following does not reflect the OP's actual question, this is more like a suggestion for re-considering the design. Why use "by reference" parameter passing to begin with, here?
Why not simply stick with a functional style using return and continuously updating the proc-local variable c within the loop body's script?
proc one { a c } {
for {set i $a} {$i < 10} {incr i} {
set c [two $c] ;# <= note: passing 'c' by value
puts "Value of c is $c"
}
}
proc two { c_value } {
incr c_value
return $c_value
}
one 0 3
This will make two re-usable in different contexts, by different callers. Also:
It can be easily extended to multiple return values, variable updates using lassign in the caller's context (one).

How do I access a variable outside of a proc

I'm trying to wrap my head around Tcl variable scopes, but I'm stuck on what I thought would be a simple concept: how can I access a variable that I've defined outside of my proc, but that I do not explicitly pass to the proc?
I'm trying to avoid setting a bunch of global variables, and only access variables that I define within a particular namespace. What do I need to add to my code below so that the proc can access the variable a, which is obviously not in the scope of the proc?
set a apples
proc myList {b c} {
puts [concat $a $b $c]
}
You can use upvar:
set a apples
proc myList {b c} {
upvar a a
puts [concat $a $b $c]
}
Or, expanding the example a bit to show the "source" variable does not have to exist in the global scope:
proc p1 {} { set a 10; p2 }
proc p2 {} { upvar 1 a b; puts "in p2, value is $b" }
p1
outputs
in p2, value is 10
If a was defined in a namespace, you can use variable:
namespace eval foo {
set a apples
# OR
# variable a apples
}
proc foo::myList {b c} {
variable a
puts [concat $a $b $c]
}
Or if a was created in the global scope, you can still access it without the global function, using :: (I'll reference this SO question for this one):
proc myList {b c} {
puts [concat $::a $b $c]
}

How to declare a data member reference in tclOO

I am trying to hold a reference to a variable as a data member in tclOO. Is there any construct that can simulate the upvar command that is used inside functions.
oo::class create TestClass {
variable refToVar
constructor {varRef} {
set varRef [string range $varRef 1 [expr [string length $varRef] -1]]
# upvar $varRef temp
# set refToVar $temp
upvar $varRef refToVar
}
method getRefToVar {} {
return $refToVar
}
method setRefToVar {value} {
set refToVar $value
}
}
proc testProc {varRef} {
set varRef [string range $varRef 1 [expr [string length $varRef] -1]]
upvar $varRef temp
set temp 5
}
puts "start"
set x 100
puts $x
puts "proc test"
testProc {$x}
puts $x
puts "tclOO test"
TestClass create tst {$x}
puts [tst getRefToVar]
tst setRefToVar 20
puts [tst getRefToVar]
puts $x
Basically i want to achieve the same behaviour with the oo::class that I am doing with the proc.
The equivalent is upvar. However you need to run the upvar in the right context, that of the object's state namespace and not the method (because you want refToVar to be by the overall object) because upvar always makes its references from in the current stack context, which means local variables in both cases by default.
TclOO provides the tools though.
my eval [list upvar 2 $varref refToVar]
It's a 2 because there's one level for my eval and another level for the method. MAKE SURE that you only refer to variables in a namespace (global variables are in the global namespace) this way or you'll have pain. It's probably not a good idea to keep references to variables around inside an object as they can be run from all sorts of contexts, but it should work.
You can also use the namespace upvar command inside that generated my eval to do the same sorts of thing, since anything you can write that is sensible can be described using namespace/varname coordinates instead of stack-index/varname coordinates.
[EDIT]: In fact, Tcl takes care to avoid the evil case, and throws an error if you try. Here's a boiled-down version of the class:
oo::class create Foo {
constructor v {
# This is done like this because this sort of operation is not affected
# by the 'variable' declaration; it's a bit unusual...
my eval [list upvar 2 $v ref]
}
variable ref
method bar {} {
# Just some normal usage
puts $ref
}
}
Let's show this working normally:
% set x 123
123
% set y [Foo new x]
::oo::Obj12
% $y bar
123
% incr x
124
% $y bar
124
OK, that all looks good. Now, let's go for attaching it to a local variable:
proc abc {} {
set x 234
set y [Foo new x]
# Try the call
$y bar
incr x
$y bar
# Try returning it to see what happens with the lifespan
return $y
}
But calling it doesn't work out:
% abc
bad variable name "ref": can't create namespace variable that refers to procedure variable
% puts $errorInfo
bad variable name "ref": can't create namespace variable that refers to procedure variable
while executing
"upvar 2 x ref"
(in "my eval" script line 1)
invoked from within
"my eval [list upvar 2 $v ref]"
(class "::Foo" constructor line 1)
invoked from within
"Foo new x"
(procedure "abc" line 3)
invoked from within
"abc"
So there you go; you can link an external global (or namespace) variable easily enough into an object, but it had better not be a local variable. It might be possible to do extra work to make using a local variable that is actually an upvar to a global variable work, but why would you bother?
Donal has answered your question, but there is a better way to do it.
oo::class create TestClass {
variable varRef
constructor args {
lassign $args varRef
}
method getRefToVar {} {
upvar 0 $varRef refToVar
return $refToVar
}
method setRefToVar {value} {
upvar 0 $varRef refToVar
set refToVar $value
}
}
Here, you supply the TestClass object with the qualified name of the variable, like ::x for the global variable x, or ::foobar::x for the x variable in the foobar namespace. You save this name in the instance variable varRef.
When you want to read from or write to this variable, you first create a local reference to it:
upvar 0 $varRef refToVar
This means that in the method where you are invoking this command, refToVar is a reference or alias to the variable whose name is stored in varRef.
You can do the same thing in a command procedure:
proc testProc {varRef} {
upvar 0 $varRef refToVar
set refToVar 5
}
Testing it:
puts "start"
set x 100
puts $x
puts "proc test"
testProc ::x
puts $x
puts "tclOO test"
TestClass create tst ::x
puts [tst getRefToVar]
tst setRefToVar 20
puts [tst getRefToVar]
puts $x
Note how the names are written differently.
If you somehow don't know the qualified name of the variable, this should be helpful:
TestClass create tst [namespace which -variable x]
BTW: you don't have to write
string range $varRef 1 [expr [string length $varRef] -1]
there is an abbreviated form for that:
string range $varRef 1 end
but you don't have to write that either: to remove zero or more dollar signs from the left end of a string, just
string trimleft $varRef \$
but you don't have to write that either: you don't need to add the dollar sign when passing a variable name:
proc bump varName {
upvar 1 $varName var
incr var
}
set x 10
# => 10
bump x
set x
# => 11
Documentation: expr, incr, lassign, namespace, proc, puts, set, string, upvar

is it possible to have static variable inside a rebol function?

This shows how to have a static variable inside an object or context:
http://www.mail-archive.com/list#rebol.com/msg04764.html
But the scope is too large for some needs, is it possible to have a static variable inside an object function ?
Or you can use FUNCTION/WITH. This makes the function generator take a third parameter, which defines a persistent object that is used as the "self":
accumulate: function/with [value /reset] [
accumulator: either reset [
value
] [
accumulator + value
]
] [
accumulator: 0
]
To use it:
>> accumulate 10
== 10
>> accumulate 20
== 30
>> accumulate/reset 0
== 0
>> accumulate 3
== 3
>> accumulate 4
== 7
You may also want to look at my FUNCS function.
In Rebol 3, use a closure (or CLOS) rather than a function (or FUNC).
In Rebol 2, fake it by having a block that contains your static values, eg :
f: func [
/local sb
][
;; define and initialise the static block
sb: [] if 0 = length? sb [append sb 0]
;; demonstate its value persists across calls
sb/1: sb/1 + 1
print sb
]
;; sample code to demonstrate function
loop 5 [f]
== 1
== 2
== 3
== 4
== 5