Tcl/Tk: scope of variables for a function within a function - scope

I'm getting lost on scoping variables in Tcl. I've got two procs I've written and stored in a file that I want to call in another script I'm writing. In my sourced file (which I'll call bar.tcl), I'm using upvar to define a bunch of variables at the beginning of my file that I plan to call within the procs I've defined within bar.tcl, which I'll call bar1 and bar2
I want to call the functions within bar.tcl from my code within foo.tcl. My functions work fine if I'm just interacting with bar.tcl in tclsh, but as soon as I call bar1 or bar2 from within a function in foo.tcl, I get an error can't read "alpha": no such variable
It seems I'm using upvar incorrectlys. Is this even the right tool for the job? What can I do so that I can achieve the following?
define a bunch of variables at the beginning of bar.tcl that I want to use in both procs bar1 and bar2, and
call bar1 and bar2 from within procs defined in foo.tcl
Here's my code in foo.tcl:
# foo.tcl
source bar.tcl
proc foo {fooValues} {
foreach fooValue $fooValues {
set myNewValue [bar1 $fooValue]
return $myNewValue
}
}
set myOldValues {5 10 15 20 25}
foo myOldValues
And here's my code in bar.tcl:
set a apples
set b bananas
set c cherries
set d dates
proc bar1 {bar} {
upvar a alpha
upvar b bravo
upvar c charlie
upvar d delta
set myReturnValue "$bar $alpha $bravo $charlie $delta"
return $myReturnValue
}
proc bar2 {bar} {
upvar a alpha
upvar b bravo
upvar c charlie
upvar d delta
set myReturnValue "$alpha $bravo $charlie $delta $bar"
return $myReturnValue
}

When bar1, bar2, ... are called from within foo, their upvar calls will try to link with any proc-local variables of the executed foo proc. This is because upvar without level defaults to upvar 1 which denotes the proc's caller (i.e., foo). Your targeted variables in bar.tcl, however, reside in the global namespace and not within foo. Hence, there are no variables linked by alpha, bravo.
Rewrite your upvar occurrences as upvar #0 ..., with #0 indicating the global, top-level namespace, e.g.:
upvar "#0" a alpha
This is equivalent to using global a modulo a variable alias (alpha).

Related

Tcl: Why is the dollar sign used in the first argument to `upvar`?

The tcl docs provide the following example on how to use the upvar command:
proc add2 name {
upvar $name x
set x [expr {$x + 2}]
}
Inside this procedure, x acts as a reference to the variable name which belongs to the scope outside of add2.
I don't understand why the first argument to upvar has to be provided with the dollar sign, and the second argument not. Shouldn't it simply be upvar name x, since we are referencing name rather than its value?
Here's an example of how add2 might be used:
(Tcl) 2 % set fred 3
3
(Tcl) 3 % add2 fred
5
Here add2 is called with its parameter called "name" set to the value "fred". If the code then did upvar name x it would be operating on a variable literally called "name" but we need it to operate on the variable called "fred" so we write $name to get the value "fred" from the parameter called "name".
Donal's answer might be illustrated this way:
proc addfred {} {
upvar fred fr
incr fr 2
}
% set fred 3
3
% addfred
5
proc addfred {} {
upvar "fred" fr
incr fr 2
}
% addfred
7
I.e. you need not have the upvar-ed variable in the proc's parameters at all, to make it work.
With "level" option of upvar, you can add even more resourcefulness to this.

Python, are list and string variables actually scoped differently?

I'm learning about scope in Python 3, and this example has me confused. Comparing behaviors of a list variable and a string variable when called inside a function:
foo1 = []
foo2 = ''
def f():
foo1.append(3)
global foo2
foo2 += 'c'
print('foo1-in-f:',foo1)
print('foo2-in-f:',foo2)
print('foo1-before:',foo1)
print('foo2-before:',foo2)
f()
print('foo1-after:',foo1)
print('foo2-after:',foo2)
The output, as expected, is:
foo1-before: []
foo2-before:
foo1-in-f: [3]
foo2-in-f: c
foo1-after: [3]
foo2-after: c
I'm confused why the string must be declared global, as in the line global foo2, but the list is not declared global, as in there in no line global foo1.
I ran the code omitting the line global foo2 and unsurprisingly got UnboundedLocalError: local variable 'foo2' referenced before assignment. But, why am I not getting this error for foo1?
Any insights appreciated. I want to make sure I'm understanding how this really works.
In a Python function, all variable references are assumed to be global unless named locally. All new objects are created in local scope and there are restrictions on transferring or modifying objects to another scope.
You can do:
a=1
def f(): return a+1 # unnamed integer object created and returned
>>> f()
2
You can modify the contents of a global mutable variable since this does not assign a locally named object to the global scope:
ls=['string']
def f():
ls.append('another') # unnamed string object created and added to ls
ls[0]+='_modified' # ls[0] read, new object created with +=,
# new object added to ls[0]
>>> f()
>>> ls
['string_modified', 'another']
But it would be an error to use ls+=[something] since the assignment += is treated as ls being local in scope then reassigned to the global scope:
ls=[]
def f():
ls+=['new entry'] # UnboundLocalError
The issue you are seeing is not necessarily modifying global variables. It is with reassigning the name that the function has used locally to the global scope.
There is a FAQ on this issue on the Python website.
There is an expanded FAQ on Eli Bendersky's blog.
You need to know how variable scopes work in Python. Python does not require you to declare variables, but assumes that a variable assigned in the body of a function is local. You can see this reflected by the compiler in the generated bytecode:
foo1 = []
foo2 = ''
def f():
foo1.append(3)
foo2 += 'c'
from dis import dis
dis(f)
4 0 LOAD_GLOBAL 0 (foo1)
2 LOAD_METHOD 1 (append)
4 LOAD_CONST 1 (3)
6 CALL_METHOD 1
8 POP_TOP
5 10 LOAD_FAST 0 (foo2)
12 LOAD_CONST 2 ('c')
14 INPLACE_ADD
16 STORE_FAST 0 (foo2)
18 LOAD_CONST 0 (None)
20 RETURN_VALUE
foo1 is loaded from the global context because foo1.append(3) is an item assignment operation - it modifies the actual reference. AFAIK Python strings are immutable which means that you need copy the value before doing the assignment, which is what you are doing inside the function because foo2 += 'c' will actually create a new string. Try running foo1 += [3] and you will get the same UnboundLocalError for foo1.
foo1.append(3) is an item assignment operation which is equivalent to foo1[len(foo1):] = [3]. This kind of operation is not possible for Python string for reasons stated above - try running foo2[:] = 'c' and you will get the error 'str' object does not support item assignment.
Now, the global keyword basically tells the interpreter to treat foo2 as a global variable in spite of the assignment within the function.

Dynamic and Static Scoping

Me and my friend are having some trouble in regards to understanding Static and Dynamic scoping. I believe with dynamic, the variable (global) will keep being updated by other functions until printed, whereas with static I think that whatever value get's assigned to a variable first stays that way.
Is this thinking correct or no?
For an example using my thoughts above I have calculated the following from this code snippet.
int a, b, c;
void p() {
int a = 3;
b = 1;
c = a + b;
q();
}
void print() { printf(ā€œ%d %d %d\nā€, a, b, c); }
void q() {
int b = 4;
a = 5;
c = a + b;
print();
}
main() {
int c = 5;
p();
}
Output with static scoping: 315
Output with dynamic scoping: 549
With static scoping, print would fail because neither a, b, nor c are assigned values either inside print or at the scope where print is defined (namely, the first line of the file).
With dynamic scoping, the output would be 549, since each of a, b, and c has a value assigned in q. Not demonstrated by your code is also the fact that after q returns from its call inside p, the local variable a has the value 5 set in q, not the global variable. Namely, the following occurs:
Global variables a, b, and c are declared, but do not have values. Let's assume your language initializes such values to 0.
main is called. A variable c local to main is given the value 5; global c still equals 0.
p is called. A p-local variable a is assigned the value 3; global a is still 0.
No local variable b exists in p or its caller, main, so the global b is set to 1.
No local variable c exists in p, but one does in c, to its value is set to 3 + 1 = 4.
q is called. A local b is declared and set to 4, leaving global b set to 0.
No local variable a exists in q, but one does in its caller p, so that value changes from 3 to 5.
No local variable c exists in q or its caller p, but does in p's caller main, so that value is set to 5 + 4 = 9. Global c is still 0.
print is called, and lacking any local a, b, or c, it looks back in its call chain. It uses a from p, b from q, and c from main (none of the globals are used.
q returns. In p, the values of a and c are still 5 and 9 as set in q. b is still 1, since q declared a local b.
p returns. In main, we still have a=0 (since p declared its own copy before calling q), b=1 (since p modified the global b), and c=9 (since q ultimately modified the variable local to c).
main returns. We still have global a=0, b=1, and c=0.
If that's confusing (and I didn't confuse myself and make any mistakes), you might understand why most languages use static scoping: it's not only much easier, but possible, to reason about the behavior of the program without having to run or simulate it just to track variable assignments.
static scoping- 5 1 9// it takes global values as variables not defined within print function
dynamic scoping- 5 4 9

Lexical scoping vs dynamic scoping

So I have this problem where I have to figure out the output using two different scoping rules. I know the output using lexical scoping is a=3 and b=1, but I am having hard time figure out the output using dynamic scoping.
Note:the code example that follows uses C syntax, but let's just treat it as pseudo-code.
int a,b;
int p() {
int a, p;
a = 0; b = 1; p = 2;
return p;
}
void print() {
printf("%d\n%d\n",a,b);
}
void q () {
int b;
a = 3; b = 4;
print();
}
main() {
a = p();
q();
}
Here is what I come up with.
Using Dynamic scoping, the nonlocal references to a and b can change. So I have a=2 ( return from p() ), then b=4 ( inside q() ).
So the output is 2 4?
As we know, C doesn't have dynamic scoping, but assuming it did, the program would print 3 4.
In main, a and b are the global ones. a will be set to 2, as we will see that this is what p will return.
In p, called from main, b is still the global one, but a is the one local in p. The local a is set to 0, but will soon disappear. The global b is set to 1. The local p is set to 2, and 2 will be returned. Now the global b is 1.
In q, called from main, a is the global one, but b is the one local in q. Here the global a is set to 3, and the local b is set to 4.
In print, called from q, a is the global one (which has the value 3), and b is the one local in q (which has the value 4).
It is in this last step, inside the function print, that we see a difference from static scoping. With static scoping a and b would be the global ones. With dynamic scoping, we have to look at the chain of calling functions, and in q we find a variable b, which will be the b used inside print.
C is not a dynamically scoped language. If you want to experiment in order to understand the difference, you're better off with a language like Perl which lets you chose between both.

How to get a reference on the Itcl class member variable?

Say I have the following structure:
package require Itcl
itcl::class AAA {
private variable m_list {}
constructor {} {
fill m_list list
}
}
How to get a reference on the m_list in order to write
foreach elem $reference {.......}
Consider that list is really big and I don't want to copy it!
Tcl variables use copy-on-write semantics. You can safely pass a value around, assigning multiple variables to it, without worrying about it taking up more space in memory.
For example
set x {some list} ;# there is one copy of the list, one variable pointing at it
set y $x ;# there is one copy of the list, two variables pointing at it
set z $y ;# there is one copy of the list, three variables pointing at it
lappend z 123 ;# there are two copies of the list
;# x and y pointing at one
;# z pointing at the other
;# which is different from the first via an extra 123 at the end
The above code will result in two giant lists, one with the original data that both x any y point at, and one with the extra element of 123 that only z points to. Prior to the lappend statement, there was only one copy of the list and all three variables pointed at it.
Here is how to get a reference on the member of a class:
package require Itcl
itcl::class AAA {
public variable m_var 5
public method getRef {} {
return [itcl::scope m_var]
}
}
AAA a
puts [a cget -m_var]
set [a getRef] 10
puts [a cget -m_var]

Resources