Tcl variable substitution inside $:: - variables

If I define the following list:
set ::listofvariablesn [list {"inputfile" "Input file"}]
set ::inputfile "Somefile or numbers"
I can check if the variable ::inputfile exists using:
foreach a $::listofvariablesn {
if {[info exists ::[lindex $a 0]]} {
puts "Ok"
}
}
But I cannot access the value of ::inputfile using:
foreach a $::listofvariablesn {
if {[checkvl "Input file" $::[lindex $a 0] 1 0 0 0]} {
puts "Ok"
}
}
I got the error:
can't read "::": no such variable
Is there a way to get this substitution done? I mean turn $::[lindex $a 0] into $::inputfile so I can access the value of ::inputfile
Thanks a lot

The $ is a shortcut, and can't do double substitution.
Use the set command:
if {[checkvl "Input file" [set ::[lindex $a 0]] 1 0 0 0]} {

Apart from the longhand form of $, the one-argument form of the set command, there is also the option to create an alias of the variable (with a known name) using upvar 0. For example:
upvar 0 $a b
puts "exists: [info exists b]"
puts "value: $b"
Anything you do to the variable b (except changing the upvar) will be performed on the variable that was named by $a.

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*
}

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

How do I parse and validate command line arguments in Raku (formerly known as Perl 6)?

In Perl 5, I can use Getopt::Long to parse commandline arguments with some validation (see below from http://perldoc.perl.org/Getopt/Long.html).
use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose;
GetOptions ("length=i" => \$length, # numeric
"file=s" => \$data, # string
"verbose" => \$verbose) # flag
or die("Error in command line arguments\n");
say $length;
say $data;
say $verbose;
Here =i in "length=i" creates a numeric type constraint on the value associated with --length and =s in "file=s" creates a similar string type constraint.
How do I do something similar in Raku (née Perl 6)?
Basics
That feature is built into Raku (formerly known as Perl 6). Here is the equivalent of your Getopt::Long code in Raku:
sub MAIN ( Str :$file = "file.dat"
, Num :$length = Num(24)
, Bool :$verbose = False
)
{
$file.say;
$length.say;
$verbose.say;
}
MAIN is a special subroutine that automatically parses command line arguments based on its signature.
Str and Num provide string and numeric type constraints.
Bool makes $verbose a binary flag which is False if absent or if called as --/verbose. (The / in --/foo is a common Unix command line syntax for setting an argument to False).
: prepended to the variables in the subroutine signature makes them named (instead of positional) parameters.
Defaults are provided using $variable = followed by the default value.
Aliases
If you want single character or other aliases, you can use the :f(:$foo) syntax.
sub MAIN ( Str :f(:$file) = "file.dat"
, Num :l(:$length) = Num(24)
, Bool :v(:$verbose) = False
)
{
$file.say;
$length.say;
$verbose.say;
}
:x(:$smth) makes additional alias for --smth such as short alias -x in this example. Multiple aliases and fully-named is available too, here is an example: :foo(:x(:bar(:y(:$baz)))) will get you --foo, -x, --bar, -y and --baz and if any of them will pass to $baz.
Positional arguments (and example)
MAIN can also be used with positional arguments. For example, here is Guess the number (from Rosetta Code). It defaults to a min of 0 and max of 100, but any min and max number could be entered. Using is copy allows the parameter to be changed within the subroutine:
#!/bin/env perl6
multi MAIN
#= Guessing game (defaults: min=0 and max=100)
{
MAIN(0, 100)
}
multi MAIN ( $max )
#= Guessing game (min defaults to 0)
{
MAIN(0, $max)
}
multi MAIN
#= Guessing game
( $min is copy #= minimum of range of numbers to guess
, $max is copy #= maximum of range of numbers to guess
)
{
#swap min and max if min is lower
if $min > $max { ($min, $max) = ($max, $min) }
say "Think of a number between $min and $max and I'll guess it!";
while $min <= $max {
my $guess = (($max + $min)/2).floor;
given lc prompt "My guess is $guess. Is your number higher, lower or equal (or quit)? (h/l/e/q)" {
when /^e/ { say "I knew it!"; exit }
when /^h/ { $min = $guess + 1 }
when /^l/ { $max = $guess }
when /^q/ { say "quiting"; exit }
default { say "WHAT!?!?!" }
}
}
say "How can your number be both higher and lower than $max?!?!?";
}
Usage message
Also, if your command line arguments don't match a MAIN signature, you get a useful usage message, by default. Notice how subroutine and parameter comments starting with #= are smartly incorporated into this usage message:
./guess --help
Usage:
./guess -- Guessing game (defaults: min=0 and max=100)
./guess <max> -- Guessing game (min defaults to 0)
./guess <min> <max> -- Guessing game
<min> minimum of range of numbers to guess
<max> maximum of range of numbers to guess
Here --help isn't a defined command line parameter, thus triggering this usage message.
See also
See also the 2010, 2014, and 2018 Perl 6 advent calendar posts on MAIN, the post Parsing command line arguments in Perl 6, and the section of Synopsis 6 about MAIN.
Alternatively, there is a Getopt::Long for perl6 too. Your program works in it with almost no modifications:
use Getopt::Long;
my $data = "file.dat";
my $length = 24;
my $verbose;
get-options("length=i" => $length, # numeric
"file=s" => $data, # string
"verbose" => $verbose); # flag
say $length;
say $data;
say $verbose;

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