A part of my code in tcl is:
proc Frame {columnLine} {
.
.
.
}
Now I want use $variable in parenthesis. For example:
set x 2.
set columnLine {$x 5. 10. 15.}
However, after running Tcl, I face an error! How I do solve this problem?
Tcl does not do substitutions on things inside {braces}. If you want substitutions, you've got to either put the overall word inside "double quotes", or use the subst command:
set x 2.
set columnLine [subst {$x 5. 10. 15.}]
set x 2.
set columnLine "$x 5. 10. 15."
One advantage of using subst is that you can pick to just substitute variables and leave backslashes and [bracketed command calls] alone. This is sometimes very useful indeed.
set x 2.
set columnLine [subst -nobackslashes -nocommands {$x 5. 10. 15.}]
Related
This question already has an answer here:
Double variable substitution in tcl
(1 answer)
Closed 3 months ago.
Let's say I have this code :
set a 1
set a.b a
set thing a.b
puts [subst $$thing]
The answer I would expect on the last line would be "a", but tcl answers 1.b
I tried to put \ everywhere before the . but it didn't changed anything.
Is there away to get a from thing variable ?
Tcl does not double evaluate two consecutive dollar signs.
The $thing characters in your command subst $$thing are first replaced by the value of $thing, which is a.b.
Subsequently, the subst command is evaluated like this:
subst $a.b
The above subst command replaces $a with 1, which explains why you get 1.b returned.
A reliable way to do multiple variable interpolation is with the set command without a second argument. Chain together multiple set commands to interpolate multiple times.
puts [set thing]
--> a.b
puts [set [set thing]]
--> a
puts [set [set [set thing]]]
--> 1
button .mltext.button -text "Apply" -command {set top_tlbl [update_text $top_tlbl $spc ] }
I get an error :
can't read "spc": no such variable
while executing
"update_text $top_tlbl $spc "
invoked from within
".mltext.button invoke"
How can I pass the values of the variables to the update_text function?
Maybe I can start by understanding this :
(System32) 3 % expr 2 + 2
4
(System32) 4 % list expr 2 + 2
expr 2 + 2
(System32) 5 % [list expr 2 + 2]
invalid command name "expr 2 + 2"
(System32) 6 %
According to me, the last one should generate expr 2 + 2, which is the same as the first command - so, why does TCL have a problem?
Thanks..
In your examples with expr, we start by counting the words.
expr 2 + 2 has four words: expr, 2, + and 2. The first one is the command name, expr, and the others are passed as arguments; the documentation for expr says that it concatenates its arguments and evaluates the resulting expression.
list expr 2 + 2 has five words: list, expr, 2, + and 2. The first one is the command name, list, and the others are passed as arguments; the documentation for list say that it returns the list that has its arguments as elements. Though we don't see it here explicitly, what this does is introduce exactly the quoting needed to make a single substitution-free command. Good Tcl code uses list quite a lot when generating code.
[list expr 2 + 2] has one word, which is the result of calling list expr 2 + 2. If you just feed that into Tcl directly, it has a weird but legal command name, and you probably don't have a command called that. Hence you get an error.
Now let's consider:
button .mltext.button -text "Apply" -command {set top_tlbl [update_text $top_tlbl $spc]}
From your comments, you're calling this inside a procedure. This means that you need to bind the variables inside the callback (which is evaluated inside uplevel #0, and probably long after your current procedure has returned) without doing the callback immediately. Curiously, it looks like you're changing top_tlbl at runtime too.
Let's start by thinking about the innermost part. We can generate it with list just fine (ignoring the different lifetime bindings):
list update_text $top_tlbl $spc
Now we've just got to make the other parts work as well. That's where it gets fiddly and you end up with something like this:
… -command "set top_tlbl \[[list update_text $top_tlbl $spc]\]"
Now let's fix the lifetime stuff:
… -command "set top_tlbl \[update_text \$top_tlbl [list $spc]\]"
This sort of thing is more than a bit error-prone in complicated callbacks! At that point, it's much easier (and good practice) to have a little helper procedure:
proc do_update_text {varname value} {
upvar #0 $varname var
set var [update_text $var $value]
}
Then you can do (note: passing the name of top_tlbl, not the value):
… -command [list do_update_text top_tlbl $spc]
The use of global variable names might be considered to be problematic, except you've got to remember that the global namespace is mainly owned by your application. If you want to store variables in globals for your app, do it! You have the complete right.
Library code needs to be a bit more careful of course. The usual techniques there include using variables in other namespaces (Tk does this internally) or in objects. The code doesn't really get that much more complicated; it's still building on the practice of using helper procedures listed above. Passing a namespaced variable name is pretty easy though:
… -command [list do_update_text ::mynamespace::top_tlbl $spc]
-command {set top_tlbl [update_text $top_tlbl $spc]} will be invoked in global context when the button is clicked. Is your variable spc accessible in global context? Try to specify the variable's namespace explicitly, for example $::spc.
expr 2 + 2 and "expr 2 + 2" are not the same. Tcl identifiers can contain spaces and "expr 2 + 2" is the whole identifier, which is not found.
You should probably just call update_text and have top_tlbl be a global and set from the called function rather than returned.
Its generally simplest to just try and call a simple function and use list to ensure everything is quoted correctly.
button .mltext.button -text "Apply" \
-command [list update_text $top_tlbl $spc]
This will capture the current values of top_tlbl and spc when you define this button and its command. If you want the values at the time you press the button then you should probably be passing the names of the variables and using the variable or global commands.
Anyway, the rules are that things inside curly braces {} have no substututions performed on them. So {$v} is passed to the callee as exactly that - dollar v. Using list or double quotes lets variable substitution be performed and so you pass the value of the variable. List properly handles quoting for cases where the variable value might contain spaces and so on.
I have a loop that looks something like:
foreach x {a b} {
set type_$x [some_function_here]
set N_$x [function type_$x]
}
The problem is that I want to dereference type_$a in the second line and use its value as the function argument.
However doing:
set N_$x [function $type_$x]
doesn't work and neither did any of the other combinations I tried with the subst command did either.
How do I solve this?
Three possibilities, in order of how much I think you should prefer them.
Arrays
By far the easiest technique is to use an array instead:
foreach x {a b} {
set type($x) [some_function_here]
set N($x) [function $type($x)]
}
This does change how the rest of your program sees things, so it's not a zero-impact technique, but it's very easy; I think this is the most recommended way of doing this.
Local Aliases
Alternatively, use upvar 0 to make a local alias to the variable-named variable:
foreach x {a b} {
upvar 0 type_$x typex N_$x Nx
set typex [some_function_here]
set Nx [function $typex]
}
Internally, the names just resolve to the same storage cell, so this is an efficient technique (though upvar 0 is quite tricky!)
Single-Argument set
Finally, you can read from an arbitrarily-named variable using the set command with one argument; the $ syntax is arguably just syntactic sugar for that.
foreach x {a b} {
set type_$x [some_function_here]
set N_$x [function [set type_$x]]
}
It's usually something of a code-smell if you're doing that a lot, and it usually indicates that you should be using an array.
set N_$x [function [set type_$x]]
The $var notation is basically shorthand for the command [set var], and sometimes you need to use the command rather than the shorthand.
There is no way to tell the command evaluation "when I say '$type_$x' I want you to hold up evaluating the first $ until the whole variable name has been put together". That is, unless you write it as [set type_$x], in which case the name type_a or type_b is constructed first, and then passed to set.
Documentation: set, evaluation syntax
Some others ways to do a deference of a variable with dynamic name.
% set x a
a
% set type_$a stackoverflow
stackoverflow
% expr \$type_$a
stackoverflow
% subst $[subst type_$a]
This is bit complex, comparable to other methods though.
When a variable is substituted and the value is a list, is it possible that each value of the list is regarded as one argument?
Here is an example code:
set a "-nonewline hi"
puts $a
What I really want is after the substitution, the puts command becomes puts -nonewline hi instead of puts "-nonewline hi".
Is it possible?
Yes, you can use something like this:
puts {*}$a
In versions prior to 8.5, you would have to use eval:
eval puts $a
subst or eval command is not working in my case ..
% proc sum {a b} {
return [expr $a+$b]
}
%
% set a 1
1
% set b 2
2
% sum $a $b
3
%
% sum {$a} {$b}
can't use non-numeric string as operand of "+"
%
% subst [sum {$a} {$b}]
can't use non-numeric string as operand of "+" >>>>>>>>> Why i am unable to substitue the value
%
% eval [sum {$a} {$b}]
can't use non-numeric string as operand of "+" >>>>>>>>> Why i am unable to substitue the value
%
I want to know why the above cases are not working for me .. subst command should do the variable and command substitution . But why my variables are not substituting.
Can anyone Please Explain what is going on?
First, you do understand why “$a“ isn't a value you can add to? It's not a number at all. (I don't mean $a, the instruction to read from a variable and substitute it, I mean the string consisting of a $ followed by an a.)
When you put braces round things in Tcl, it means “don't do anything with this string at all; use it as-is”. Always. (Sometimes you're feeding that string into a command that evaluates, but not always.) When you put square brackets round things, it means evaluate that string as a script immediately and use the result of the script as the value to substitute. Always.
When you do:
subst [sum {$a} {$b}]
You need to understand that the call to sum is done while assembling the arguments to subst. That call produces an error, so the call to subst never happens. Similarly with the eval form you used.
If we use a somewhat less surprising form:
subst {sum {$a} {$b}}
Then you'll get this out: sum {1} {2}. subst doesn't understand the overall string as a script. On the other hand, with:
eval {sum {$a} {$b}}
In this case you get an error not from the eval as such, but rather from the fact that the call to sum inside is still erroneous.
I suppose you could do:
eval [subst {sum {$a} {$b}}]
But really don't. There's got to be a simpler and less error-prone way.
You put the square braces [] to the wrong place (resp. you even don't need them in the eval case). In the way you wrote the commands the sum {$a} {$b}] is evaluated before the subst or eval command could evaluate the contents of $a and $b.
Correct is:
eval sum {$a} {$b}
or
sum [subst {$a}] [subst {$b}]
You don't understand Tcl correctly.
subst takes a string and substitues all variables in it, right.
But you pass the result of sum {$a} {$b} into it, which fails.
So you could either subst each parameter before you call sum:
sum [subst {$a} {$b}]
Or modify the sum to do the evaluation for you:
proc sum {a b} {
uplevel 1 [list expr $a + $b]
}
expr does an own round of evaluation, this is why you usually pass a litteral string (enclosed with {}) to it, but in this case we actually use this fact. uplevel executes the command in the context of the caller.
If you call a command, Tcl will replace the variables before the actuall call is made, so maybe this snippet could help you to understand Tcl a little bit better:
set a pu
set b ts
$a$b "Hello World!"