What method for constracting an object is more efficient? - tcl

I have a class that has several inner values:
private variable layer_type
private variable color
private variable name
private variable thickness
private variable conductivity
private variable permittivity
private variable loss_tangent
I want to initiate them with values that the user gives to the constructor, but since there 7 of them, to reduce clutter and code size I did it in a loop:
constructor {in_layer_type in_color in_conductivity in_loss_tangent in_name in_permittivity in_thikness f_row frame} {
foreach var [list layer_type color conductivity loss_tangent name permittivity thikness] {
set $var [set in_$var]
}
Is there any difference (performance wise) with this approach, over the writing all the set commands?
set layer_type $in_layer_type
set color $in_color
#...
set thickness $in_thickness

In strict terms of efficiency, writing it all out in full is the most efficient method. I suggest that you don't do that! The problem is that it's difficult to maintain and hard to use (I don't know about you, but I hate calls with lots of mandatory positional arguments in all languages!) Instead, try something like this:
constructor {f_row frame args} {
foreach {var value} $args {
if {[string match -* $var]} {
set [string range $var 1 end] $value
}
}
# ...
}
Yes, you need to write things a bit different to use this:
TheClass theObjToMake $theFRow $theFrame -layer_type 12345 ...
You're also advised to use Itcl's facilities for setting defaults if you're doing this. Pick something sensible and it will all suddenly seem very natural.

Related

Is is possible to set the default values of parameters to be the return of a function? (TCL)

In TCL is it possible to have the default values of parameters be the return value of a function call?
proc GetParameterValue { } {
# calculation for value...
return value
}
proc TestFunction { {paramVal [GetParameterValue]} } {
puts $paramVal
}
TestFunction
This results in printing "[GetParameterValue]". Rather than calling the procedure GetParameterValue. Is this possible to do in TCL or do I need to redesign this bit of code?
The default values of parameters can only be constants that you compute at the time of declaration of the procedure (most commonly, they're literals which means you don't need to use list to do the construction):
proc TestFunction [list [list paramVal [GetParameterValue]]] {
...
}
To compute a default value at procedure call time, you have to move the calculation into the body of the procedure. There's a few ways to do the detection of whether to do the calculation, but they come down to three options: using a marker value, getting a count of words in the call, and taking full control of parsing.
Using a marker value
The trick to this is to find some value that is really unlikely to be passed in. For example, if this is to be a piece of text shown to the user, a value with nothing but an ASCII NUL in it is not going to occur; put that in the default then you can tell whether you've got the default and can substitute with what the complex code provides.
proc TestFunction {{paramVal "\u0000"}} {
if {$paramVal eq "\u0000"} {
set paramVal [GetParameterValue]
}
...
}
Getting a count of words in the call
This relies on the capabilities of the info level introspection command. In particular, info level 0 reports the full list of actual arguments to the current procedure. A bit of counting, and we can can know whether a real value was passed.
proc TestFunction {{paramVal "dummy"}} {
if {[llength [info level 0]] < 2} {
# Note that the command name itself is always present
set paramVal [GetParameterValue]
}
...
}
It's a totally general approach, so there's no worry about the case where someone provides an unexpected edge case, but it's more complicated when you have multiple arguments as you need to work out how many arguments should be present and so on yourself. That is simple in this case, but gets progressively more difficult as you have more arguments.
Taking full control of parsing
Ultimately, you can also decide to make a procedure that takes full control of the parsing of its arguments. You do that by giving it a single argument, args, and then you can use any approach you want to handle the actual argument list. (I tend to not put the formal argument list in parentheses in this case only but that's just my own style.)
proc TestFunction args {
if {[llength $args] == 0} {
set paramVal [GetParameterValue]
} elseif {[llength $args] == 1} {
set paramVal [lindex $args 0]
} else {
# IMPORTANT! Let users discover how to use the command!
return -code error -errorcode {TCL WRONGARGS} \
"wrong # args: should be \"TestFunction ?paramVal?\""
}
...
}
This is currently the only way to do anything truly advanced, e.g., to have optional arguments before mandatory ones. It's also pretty much what you'd have to do in C if you implemented the command there, though adjusted for a different language. The downside is that it is definitely more work than using the built-in basic argument parsing support code provided by the implementation of the proc command.
This is meant as a complement to Donal's thorough answer. In the past, I sometimes resorted to the assistance of [subst] for computed defaults:
proc GetParameterValue {} { return computedDefault }
proc TestFunction {{paramVal [GetParameterValue]}} {
puts [subst -novariables $paramVal]
}
TestFunction; # returns "computedDefault"
TestFunction "providedValue"
TestFunction {$test}
This avoids the need for (implementing) full control over arguments, and is piggybacking onto the built-in argument handler. It also allows for using anonymous procs rather than explicitly named ones for computing the defaults:
proc TestFunction {{paramVal "[apply {{} { return computedValue }}]"}} {
puts [subst -novariables ${paramVal}]
}
TestFunction; # returns "computedDefault"
TestFunction "providedValue"
TestFunction {$test}
It goes without saying that there are also some assumptions behind, which turn into important restrictions depending on one's application case:
You must keep some discipline in using brackets for the defaults in the argument lists, and placing [subst] at the usage sites of the argument variable.
It assumes that you have some control over the arguments, or that you can guarantee that certain special-purpose characters are not valid members of the arguments' value domain.
Watch:
TestFunction {[xxx]}
throws
invalid command name "xxx"
and must be sanitized to
TestFunction {\[xxx\]}

Repeatable Parameters in Powershell Function (Preferably Linked Parameter Sets)

I am wondering if it is possible (and if so how) to create repeatable (and hopefully linked) parameters in a PowerShell function. This is how am looking for this to work:
function foo()
{
[CmdletBinding()]
Params(
[Parameter(Mandatory=$true,ParameterSetName="Default")]
[Parameter(Mandatory=$true,ParameterSetName="Set1")]
[Parameter(Mandatory=$true,ParameterSetName="Set2")]
[string]$SomeParam1,
[Parameter(Mandatory=$true,ParameterSetName="Set1")]
[Parameter(Mandatory=$true,ParameterSetName="Set2")]
*some magic here, likely, to make this repeatable*
[string]$SomeRepeatableParam,
[Parameter(Mandatory=$true,ParameterSetName="Set1")]
[string]$SomeLinkedParam1,
[Parameter(Mandatory=$true,ParameterSetName="Set2")]
[string]$SomeLinkedParam2
)
Begin
{
*some code here*
}
Process
{
foreach ($val in $SomeRepeateableParam)
{
*some code here using param and its linked param*
}
}
End
{
*some code here*
}
}
And then call this function like so:
foo -SomeParam "MyParam" -SomeRepeatableParam "MyProperty1" -SomeLinkedParam1 "Tall" -SomeRepeatableParam "MyProperty2" -SomeLinkedParam2 "Wide"
and so on, being able to use the repeatable parameter as many times in a single call as I feel like it.
Can this be done? And if so how?
Thanks for your time.
EDIT: For clarity, I don't mean an array parameter, but a repeatable parameter in which the linked parameter sets can be matched to each instance of the repeatable parameter.
Since PowerShell supports arrays as parameter values, there is generally no need to repeat a parameter.
There is no syntactic way to enforce the pairing (linking) of parameter values the way you intend, with repeating instances of the same parameter name, because parameter names must be unique (and even they didn't have to be unique, that alone wouldn't enforce the desired pairing).
You can, however, use parallel array parameters, and enforce their symmetry inside the function, e.g.:
function foo
{
[CmdletBinding()]
Param(
[string] $SomeParam1,
[string[]] $SomeRepeatableParam,
[string[]] $SomeLinkedParam
)
if ($SomeRepeatableParam.Count -ne $SomeLinkedParam.Count) {
Throw "Please specify paired values for -SomeRepeatableParam and -SomeLinkedParam"
}
for ($i = 0; $i -lt $SomeRepeatableParam.Count; ++$i) {
$SomeRepeatableParam[$i] + ': ' + $SomeLinkedParam[$i]
}
}
You would then call it as follows (note the , to separate the array elements):
foo -SomeParam1 "MyParam" `
-SomeRepeatableParam "MyProperty1", "MyProperty2" `
-SomeLinkedParam "Tall", "Wide"

Tcl upvar issue

Im trying to modify a variable using upvar (in an upward stack), but the value of the variable is passed to the procedure and not the variable name.
I cannot change what is passed, since it is already implemented widely on the program.
Is there a way to modify the file name in some way ?
proc check_file_exists {name} {
upvar $name newName
check_exists $name #Do all checks that file is there
set newName $name_1
}
check_file_exists $name
puts $name
this code will print the old name of the file and not the new one.
What I think you should do is bite the bullet and change the calls. It's a fairly simple search-and-replace, after all. The code will be more sane than if you use any of the other solutions.
check_file_exists name
Or, you could add another parameter to the parameter list and use that to pass the name, making the first argument a dummy argument.
check_file_exists $name name
Or, if you're not using the return value, you could return the new value and assign it back:
set name [check_file_exists $name]
Or, you could assign the new value to a global variable (e.g. theValue) inside the procedure, and assign that back:
check_file_exists $name
# don't need this if you're in global scope
global theValue
set name $theValue
Or, you could assign the name to a global variable (e.g. theName) and access that inside the procedure: the procedure will be able to update name directly.
# don't need this if you're in global scope
global theName
set theName name
check_file_exists $name
(There are some variations on this f.i. using upvar.)
None of the alternatives are pretty, and all of them still require you to make a change at the call (except the last, if you only ever use one variable for this value). If you're adamant about not doing that, there's always Donal's info frame solution, which only requires the procedure itself to be changed.
Let me know if you want help with the procedure code for any of these alternatives.
This is quite difficult; it's really not the way you're supposed to work. But you can do it using info frame -1 (a facility usually used for debugging) to find out exactly how the current procedure was called. However, you need to be careful as the caller might be using the result of a command: this is an unsafe hack.
proc check_file_exists {name} {
set caller [dict get [info frame -1] cmd]
if {[regexp {^check_file_exists +\$(\w+)} $caller -> varName]} {
# OK, we were called with a simple variable name
puts "Called with variable $varName"
} else {
# Complicated case! Help...
return -code error "must be called with a simple variable's contents"
}
upvar 1 $varName newName
check_exists $name
set newName $name_1
}

Reason my subroutine won't recurse

#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils 'uniq';
my %functiontable =();
$functiontable{foo} = \&foo;
sub iterate {
my ($function, $iterations, $argument) = #_;
return $argument unless 0 < $iterations;
return $argument unless $function = $functiontable{$function};
my #functioned = $function->($argument);
my #refunctioned = ();
for my $i (0 .. #functioned - 1) {
push #refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq #refunctioned;
}
sub foo {
my ($argument) = #_;
my #list = ($argument, $argument.'.', $argument.',');
return #list;
}
my #results = iterate 'foo', 2, 'the';
print "#results";
This prints the the. the,, i.e. it doesn't iterate (recurse). I would expect it to print the the. the, the.. the., the,. the,,.
(I used Smart::Comments to check whether it enters iterate a second time, and it does, but it doesn't seem to do everything in the function.)
I can't figure out why. Can someone please help me figure out why, or propose a fix?
This line:
return $argument unless $function = $functiontable{$function};
doesn't make sense. In your subroutine iterate, $function is a string and $functiontable{$function} is a reference to a subroutine. I am not sure what the purpose of this is: is it to compare against the stored function? is it to use the function referenced by the name $function?
Assuming the latter it would make more sense to simply pass in a reference to a function when you call iterate:
sub iterate {
my ($function, $iterations, $argument) = #_;
return $argument unless 0 < $iterations;
my #functioned = $function->($argument);
my #refunctioned = ();
for my $i (0 .. #functioned - 1) {
push #refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq #refunctioned;
}
my #results = iterate($functiontable{foo}, 2, 'the');
print "#results";
output:
the the. the, the.. the., the,. the,,
The problem is this line.
return $argument unless $function = $functiontable{$function};
The variable $function is being repurposed and overwritten from a string (the function name) to a code reference (the function to be executed). Later, it's passed into iterate which faithfully ignores it.
Two things would improve this code and avoid that sort of problem. First is to not repurpose variables, use two variables.
return $argument unless $function_ref = $functiontable{$function_name};
Now the mistake cannot happen. One strong indicator that you're repurposing a variable is that it changes type, like from a string to a code reference.
Note that I threw out $function entirely because it's too generic in this context. Is that the function's name or the function's reference? Neither one is obvious, so make it obvious.
Finally, iterate can be made more flexible by eliminating the function table entirely. Pass in the code reference directly. If you want a function table, write a wrapper.
sub select_iteration {
my($iteration_type, $iterations, $argument) = #_;
my $iteration_code = $iteration_types{$iteration_type};
return iterate($iteration_code, $iterations, $argument);
}
The first time your subroutine iterate is called it translates the subroutine name in $function from a name to a subroutine reference
So the first time iterate calls itself it is passing the subroutine reference, and the line
return $argument unless $function = $functiontable{$function};
will stringify the reference and attempt to find an element of the hash using a key something like CODE(0x23e0838)
Clearly that element doesn't exist, so your unless fails and $argument is returned immediately without continuing the recursion
Update
I would write something like this
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.0;
my %functions = ( foo => \&foo );
sub iterate {
my ($func, $arg, $depth) = #_;
return $arg unless $depth;
map {iterate($func, $_, $depth - 1); } $functions{$func}->($arg);
}
sub foo {
my ($arg) = #_;
map "$arg$_", '', '.', ',';
}
my #results = iterate('foo', 'the', 2);
say "#results";
output
the the. the, the. the.. the., the, the,. the,,

Why is Tcl variable command always return an empty string?

See code below:
namespace eval foo {
variable bar 5
proc getBar {} {
variable bar
}
proc getRetBar {} {
variable bar
return $bar
}
}
puts "\"[ foo::getBar ]\""
puts "\"[ foo::getRetBar ]\""
exit
It outputs:
""
"5"
Why does it not return the variable value, just like the set command would?
Why does it always return an empty string? If I want to access namespace variables through procedure, and not by accessing them directly, it makes the code slightly longer. Not a major issue, but a little annoying
That's the way the command is defined. It makes sense, because if given an odd number of arguments, the last one is a name that will be declared as a namespace variable but need not exist. If it doesn't exist, what value should variable return?
Still, it's no hassle to write a single-command getter procedure (assuming the variable exists):
proc getBar {} {
set [namespace current]::bar
}
Or you could use a TclOO object (note that you need a setter to initialize it):
oo::object create foo
# -> ::foo
oo::objdefine foo variable bar
oo::objdefine foo method setBar v {set bar $v}
oo::objdefine foo method getBar {} {set bar}
foo setBar 5
# -> 5
puts "\"[foo getBar]\""
# => "5"
You can define the object in one call, if you prefer:
oo::objdefine foo {
variable bar
method setBar v {set bar $v}
method getBar {} {set bar}
}
Documentation: namespace, oo::objdefine, oo::object, variable
The proc command returns an empty string by default. When a procedure is invoked, the procedure's return value is the value specified in a return command. If the procedure does not execute an explicit return, then its return value is the value of the last command executed in the procedure's body.
The fun part here is that variable command never returns any value. It just creates/initializes/modifies a namespace variable.
% namespace eval foo {
variable bar 5
proc getBar {} {
variable bar
}
}
If the variable command is executed inside a Tcl procedure, it creates local variables linked to the corresponding namespace variables and therefore these variables are listed by info vars.
% info vars foo::*
::foo::bar
% set foo::bar; # Getting the 'bar' value
5
% variable foo::bar 10; # Note that it does not return any value as such.
%
% set foo::bar
10
%
Since there is no implicit/explicit return value in the procedure getBar, it is returning the empty string which is the default.
Reference : variable, proc
It works that way because that's how it was implemented and documented, which is to say that it is a relatively arbitrary decision. However it is a decision that occurred nearly 20 years ago, and we see no particular reason to revisit it. Who knows what (OK, probably quite ill-advised) code would be broken?
That said, the main use of variable is with a single argument. What would be the result in this case? It's even used when the variable concerned does not exist: there is no sensible result possible at all, and unlike set it is not an error to do that (it allocates some structures in the namespace if necessary, and binds a local variable if in a procedure). The result you ask for would also make little sense when we have four arguments: why would the last thing to be set be privileged this way? The set command rejects this, and so does not need to deal with the philosophical consequences.
It is better to let variable continue to do what it currently does. It might be longer to write an explicit read as well, but it is also considerably clearer as to what the intention of the code is, and that is a thoroughly good thing over the somewhat longer term.