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

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.

Related

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.

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

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).

How to convert a hash ref in one line to a constant in perl

I'm using Sphinx::Search.
Is there is a easier way for this code example to convert a string to a constant?
use Sphinx::Search;
my $config = {
x => 'SPH_MATCH_EXTENDED2',
};
my $x = $config->{x};
print Sphinx::Search->$x(); # output: 6
I have used advice from
How do I access a constant in Perl whose name is contained in a variable?
and this example works, but if I am always using a string from a hash then do I need to put it into a separate variable to use it in this way?
my $x = $config->{x};
print Sphinx::Search->$x(); # output: 6
Is there a one- liner for this?
# does not work
print Sphinx::Search->$config->{x}();
You can create a reference to the value and immediately dereference it:
Sphinx::Search->${ \$config->{x} };
(If there are no arguments, the () is optional).
I'm guessing that SPH_MATCH_EXTENDED2 is the name of a constant that is exported by Sphinx::Search. The problem is that these are implemented as a subroutine with no parameters, so you may use them only where a bare subroutine name will be understood by Perl as a call, or where an explicit call is valid ( SPH_MATCH_EXTENDED2() )
The easiest solution is to avoid quoting the hash value at all, like so
my $config = { x => SPH_MATCH_EXTENDED2 }
and afterwards, you may use just
$config->{x}; # 6
instead of calling a pseudo class method

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]

Convert string to a variable name

I am using R to parse a list of strings in the form:
original_string <- "variable_name=variable_value"
First, I extract the variable name and value from the original string and convert the value to numeric class.
parameter_value <- as.numeric("variable_value")
parameter_name <- "variable_name"
Then, I would like to assign the value to a variable with the same name as the parameter_name string.
variable_name <- parameter_value
What is/are the function(s) for doing this?
assign is what you are looking for.
assign("x", 5)
x
[1] 5
but buyer beware.
See R FAQ 7.21
http://cran.r-project.org/doc/FAQ/R-FAQ.html#How-can-I-turn-a-string-into-a-variable_003f
You can use do.call:
do.call("<-",list(parameter_name, parameter_value))
There is another simple solution found there:
http://www.r-bloggers.com/converting-a-string-to-a-variable-name-on-the-fly-and-vice-versa-in-r/
To convert a string to a variable:
x <- 42
eval(parse(text = "x"))
[1] 42
And the opposite:
x <- 42
deparse(substitute(x))
[1] "x"
The function you are looking for is get():
assign ("abc",5)
get("abc")
Confirming that the memory address is identical:
getabc <- get("abc")
pryr::address(abc) == pryr::address(getabc)
# [1] TRUE
Reference: R FAQ 7.21 How can I turn a string into a variable?
Use x=as.name("string"). You can use then use x to refer to the variable with name string.
I don't know, if it answers your question correctly.
strsplit to parse your input and, as Greg mentioned, assign to assign the variables.
original_string <- c("x=123", "y=456")
pairs <- strsplit(original_string, "=")
lapply(pairs, function(x) assign(x[1], as.numeric(x[2]), envir = globalenv()))
ls()
assign is good, but I have not found a function for referring back to the variable you've created in an automated script. (as.name seems to work the opposite way). More experienced coders will doubtless have a better solution, but this solution works and is slightly humorous perhaps, in that it gets R to write code for itself to execute.
Say I have just assigned value 5 to x (var.name <- "x"; assign(var.name, 5)) and I want to change the value to 6. If I am writing a script and don't know in advance what the variable name (var.name) will be (which seems to be the point of the assign function), I can't simply put x <- 6 because var.name might have been "y". So I do:
var.name <- "x"
#some other code...
assign(var.name, 5)
#some more code...
#write a script file (1 line in this case) that works with whatever variable name
write(paste0(var.name, " <- 6"), "tmp.R")
#source that script file
source("tmp.R")
#remove the script file for tidiness
file.remove("tmp.R")
x will be changed to 6, and if the variable name was anything other than "x", that variable will similarly have been changed to 6.
I was working with this a few days ago, and noticed that sometimes you will need to use the get() function to print the results of your variable.
ie :
varnames = c('jan', 'feb', 'march')
file_names = list_files('path to multiple csv files saved on drive')
assign(varnames[1], read.csv(file_names[1]) # This will assign the variable
From there, if you try to print the variable varnames[1], it returns 'jan'.
To work around this, you need to do
print(get(varnames[1]))
If you want to convert string to variable inside body of function, but you want to have variable global:
test <- function() {
do.call("<<-",list("vartest","xxx"))
}
test()
vartest
[1] "xxx"
Maybe I didn't understand your problem right, because of the simplicity of your example. To my understanding, you have a series of instructions stored in character vectors, and those instructions are very close to being properly formatted, except that you'd like to cast the right member to numeric.
If my understanding is right, I would like to propose a slightly different approach, that does not rely on splitting your original string, but directly evaluates your instruction (with a little improvement).
original_string <- "variable_name=\"10\"" # Your original instruction, but with an actual numeric on the right, stored as character.
library(magrittr) # Or library(tidyverse), but it seems a bit overkilled if the point is just to import pipe-stream operator
eval(parse(text=paste(eval(original_string), "%>% as.numeric")))
print(variable_name)
#[1] 10
Basically, what we are doing is that we 'improve' your instruction variable_name="10" so that it becomes variable_name="10" %>% as.numeric, which is an equivalent of variable_name=as.numeric("10") with magrittr pipe-stream syntax. Then we evaluate this expression within current environment.
Hope that helps someone who'd wander around here 8 years later ;-)
Other than assign, one other way to assign value to string named object is to access .GlobalEnv directly.
# Equivalent
assign('abc',3)
.GlobalEnv$'abc' = 3
Accessing .GlobalEnv gives some flexibility, and my use case was assigning values to a string-named list. For example,
.GlobalEnv$'x' = list()
.GlobalEnv$'x'[[2]] = 5 # works
var = 'x'
.GlobalEnv[[glue::glue('{var}')]][[2]] = 5 # programmatic names from glue()

Resources