TCL using namespace in apply - namespaces

i have the below code
set result {}
apply {x {expr {$x*100}} ns2} {1,2,4}
In the above code what is the importance of namespace, how can i use namespace variable or store result to namespace

Let's get a namespace and a namespace variable:
% namespace eval example {variable result 0}
Use the variable command to declare the name result to be a namespace variable within the lambda:
% apply {x {variable result ; set result [expr {$x * 100}]}} 3
% set ::example::result
# => 0
% set ::result
# => 300
The lambda used the global namespace since we didn't specify any namespace.
% apply {x {variable result ; set result [expr {$x * 100}]} ::example} 3
% set ::example::result
# => 300
And there it is.
Documentation:
* (operator),
apply,
expr,
namespace,
set,
variable

Related

How do you access a procedure's local variables in a namespace eval {} inside that same procedure?

I'm sure I'm just being stupid but would you please tell me how to get access to $name inside the namespace in order to set variable n to $name? I can only find how to do this when the procedure is in the namespace but not the other way 'round. No matter what I try, this errors stating no such variable name. Thank you.
proc getNS {name} {
namespace eval ns::$name {
variable n $name
}
}
This works but isn't really an answer unless the answer is simply that it cannot be done directly. Got it from this SO question. Bryan Oakley gave the answer but used [list set...] instead of [list variable...] and that will fail if there is a global variable of the same name. (It will modify the global rather than creating a new variable in the namespace.) It may have been different, of course, in 2009 when that answer was provided.
proc getNS {name} {
namespace eval ns::$name [list variable n $name]
namespace eval ns::$name {
variable a abc
}
}
set n xyz
getNS WEBS
chan puts stdout "ns n: $ns::WEBS::n; a $ns::WEBS::a, global n: $n"
# => ns n: WEBS; a: abc; global n: xyz
You can just use set with a fully qualified variable name that uses the desired namespace:
proc getNS {name} {
namespace eval ns::$name {} ;# Create namespace if it doesn't already exist
set ns::${name}::n $name
}
getNS foo
puts $ns::foo::n ;# foo
Another way is to use uplevel to refer to the scope of the proc that calls namespace eval:
proc getNS {name} {
namespace eval ns::$name {
set n [uplevel 1 {set name}]
}
}

can I automate the calculation of the moving average for some data in tcl?

I wrote a program that calculates the moving average for some data. The problem is that I can't automatize, I mean if i want to do the process by 10 steps or more it is not smart to write 10 lines with the terms of exchange.
The section that calculates the moving average by 4 steps is:
set aux [lindex $line 4]
set T [lindex $line 1]
set aux1 [lrange $valores 1 1]
set valores [lreplace $valores 0 0 $aux1]
set aux1 [lrange $valores 2 2]
set valores [lreplace $valores 1 1 $aux1]
set aux1 [lrange $valores 3 3]
set valores [lreplace $valores 2 2 $aux1]
set aux1 [lrange $valores 4 4]
set valores [lreplace $valores 3 3 $aux1]
set valores [lreplace $valores 4 4 $aux]
set promP [avg $valores]
I know that i have to use a for loop, but the attempts i have made didn't work.
Assuming that you're keeping a window on the data, it's not too difficult. The trick is to make a procedure to do the critical work.
set WINDOW_SIZE 10
set storedData {}
proc updateMovingAverage {value} {
global storedData WINDOW_SIZE
set storedData [lreplace [list {*}$storedData $value] 0 end-$WINDOW_SIZE]
return [expr {[tcl::mathop::+ {*}$storedData] / double([llength $storedData])}]
}
Or you could create a class:
oo::class create MovingAverage {
variable window size
constructor {{windowSize 10}} {
set window {}
set size $windowSize
}
method item {value} {
set window [lreplace [list {*}$window $value] 0 end-$size]
return
}
method average {} {
return [expr {[tcl::mathop::+ {*}$window] / double([llength $window])}]
}
}
The class splits apart the adding of an item and the calculating of the average. The latter is a standard pattern in Tcl now. The trick with adding an item is to append the item to a list and then trim off the front of the list if it is larger than the desired window; the list {*}$thing $value does the append-an-item, and the lreplace THING 0 end-$wantedLength does the prefix trim (it's replacing them with an empty sequence of items).
Here's a more efficient version.
oo::class create MovingAverage {
variable window size index
constructor {{windowSize 10}} {
set window {}
set size $windowSize
set index 0
}
method item {value} {
lset window $index $value
set index [expr {($index + 1) % $size}]
return
}
method average {} {
return [expr {[tcl::mathop::+ {*}$window] / double([llength $window])}]
}
}
This uses the fact that, froom 8.6 onwards (entirely coincidentally when classes are integrated), the lset command can append an item to a list.
A coroutine version, like Donal mentioned in a comment:
#!/usr/bin/env tclsh
package require Tcl 8.6
proc moving_average {{size 10}} {
set window [list [yield [info coroutine]]]
set index 0
while {true} {
set avg [expr {[tcl::mathop::+ {*}$window] / double([llength $window])}]
set index [expr {($index + 1) % $size}]
lset window $index [yield $avg]
}
}
# Example usage, plus commented-out example usage of Donal's
# class version to compare the two.
#set class_avg [MovingAverage new]
coroutine coro_avg moving_average
for {set n 1} {$n < 20} {incr n} {
# $class_avg item $n
# puts "OO: [$class_avg average]"
puts "coro: [coro_avg $n]"
}
It and the class version have the advantage over the standard proc version like Donal's first example in that it doesn't rely on any global variables, so you can have multiple different data sets with different window sizes all being computed at the same time in different coroutines/objects.

how do you handle a variable in a deleted namespace?

Doing some exploration involving creating and deleting namespaces, I ran into this curiosity:
% namespace eval foo {variable x 1}
% namespace upvar foo x x
% set x
1
% namespace delete foo
% set x
can't read "x": no such variable
% set x 2
can't set "x": upvar refers to variable in deleted namespace
%
After this, x seems to be untouchable. It can't be read or unset because it doesn't exist, but it can't be set because it's in another namespace. How can x be recovered from this state?
What I'm trying to accomplish is importing a set of commands and variables from another namespace, and being able to remove that namespace and have the imported commands and variables go away. namespace delete seems to do the right thing with regards to imported commands, but there is no equivalent for imported variables, and as far as I can tell there isn't even any way to tell if a particular variable is imported (via namespace upvar) from somewhere else - namespace which tells you the current namespace, and namespace origin doesn't exist for variables.
(not an answer)
Looks like a bug to me => https://core.tcl-lang.org/tcl/tktnew
You can't unset the variable either. It seems like the only thing you can do is upvar it to a different variable.
% namespace eval foo {variable x 1}
% namespace upvar foo x x
% namespace delete foo
% set x
can't read "x": no such variable
% set x 1
can't set "x": upvar refers to variable in deleted namespace
% unset x
can't unset "x": no such variable
% set tmp ""
% upvar 0 tmp x
% unset x
What I'm trying to accomplish is importing a set of commands and
variables from another namespace, and being able to remove that
namespace and have the imported commands and variables go away.
As your posting is more about this buggy behaviour of the upvar mechanism (and the lacking of Tcl's introspection), your overall motivation comes only as a sidenote. Maybe you can realize sth. along the lines of the following, as an alternative (to avoid upvar)?
% namespace eval foo {variable x 1; proc bar {} {;}}
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
1
a
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
namespace "::foo" not found
% namespace eval foo {variable x 2; proc bar {} { puts BAR}}
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
2
BAR
a
You trade a namespace environment for a proc environment as an "import" target, and variable is not affected by the zombie links of upvar.
You can swap the context namespace, or re-create a deleted one as you wish.
You don't need two mechanisms (namespace import and namespace upvar), just one.
Flipping the context namespace will incur the loss of the lambdaExpr's intrep (incl. bytecode), so maybe better keep recreating the namespace ::foo.

How to read the variable Name which store the value

TCL Program Sample:
proc fun { x } {
puts "$$x = $x"
}
set a 10
fun $a
In this above program which prints the output as $10 = 10 But i would like to get a = 10 has the output. The variable which passes the values has to be read and the corresponding values as well. Is there a way to read the variable name.
proc fun name {
upvar 1 $name var
puts "$name = $var"
}
set a 10
fun a
The upvar command takes a name and creates an alias of a variable with that name.
Documentation:
proc,
puts,
set,
upvar
If you've got a currently-supported version of Tcl (8.5 or 8.6), you can use info frame -1 to find out some information about the caller. That has all sorts of information in it, but we can do a reasonable approximation like this:
proc fun { x } {
set call [dict get [info frame -1] cmd]
puts "[lindex $call 1] = $x"
}
set a 10
fun $a
# ==> $a = 10
fun $a$a
# ==> $a$a = 1010
Now, the use of lindex there is strictly wrong; it's Tcl code, not a list (and you'll see the difference if you use a complex command substitution). But if you're only ever using a fairly simple word, it works well enough.
% set x a
a
% set a 10
10
% eval puts $x=$$x
a=10
% puts "$x = [subst $$x]"
a = 10
% puts "$x = [set $x]"
a = 10
%
If you are passing the variable to a procedure, then you should rely on upvar.

How we create sink(5) variable in tcl script

i am using 20 node in ns2 , i am trying to access the a(0) a(1) a(2) a(3) a(4) variables with for loop how can i do that
here is my code
for {set i 0} {$i < $val(nn)} {incr i} {
set sink($i) [new Agent/LossMonitor]
$ns attach-agent $n($i) $sink($i)
}
but its gives an error
bad variable name "sink(0)": upvar won't create a scalar variable that looks like an array element
i declare the variable in following manner
proc record {} {
global sink(0) sink(1) sink(2) sink(3) sink(4) sink(5)
}
Just use
global sink
(global is just a special upvar case)
variables that end with (...) are (associative) arrays, using numbers as key is not recommended, it is better to use a list instead, e.g.
set mylist {}
# append some elements
lappend mylist "foo" "bar" "baz"
# get the 2nd element
puts [lindex $mylist 1]
# set the 3rd element to "Hello World"
lset mylist 2 "Hello World"