Would like to take an advice from TCL professionals for best practice.
Say you want to construct a list with a specific data by using a proc. Now which is the best way?
proc processList { myList } {
upvar $myList list_
#append necessary data into list_
}
proc returnList {} {
set list_ {}
#append necessary data into list_
return $list_
}
set list1 {}
processList list1
set list2 [returnList ]
Which one of this practices is recommended?
EDIT: I am sorry but I can't understand consensus (and explanation) of people who answered to this question.
I virtually always use the second method:
proc returnList {} {
set result {}
# ... accumulate the result like this ...
lappend result a b c d e
return $result
}
set lst [returnList]
There's virtually no difference in memory usage or speed, but I find it easier to think functionally. Also, in Tcl 8.5 you can do the splitting up of the result list relatively simply (if that's what you need):
set remainderList [lassign [returnList] firstValue secondValue]
With that, you'd end up with a in $firstValue, b in secondValue, and c d e in $remainderList.
The first option modify an existing list whereas the second create a new list. For a better comparison, I would add a parameter to returnList() and create a return value from that parameter.
Given that, the difference is in the way of passing parameters -- by reference or by value-- and in the memory budget needed by each operation.
There is no side effect with the second method, but it could be very consuming if the list is huge.
There is no general rule for recommending one over the other. My own rule is to start with the second way unless other constraints lead not to do so.
What would the syntax be for the equivalent of:
set lst [returnList]
If you wanted to return more than one list at once?
I thought that if you did something like this:
return [$list1 $list2]
It was supposed to return a list of lists, which you could then access with lindex. But, that doesn't appear to be exactly what it does. It really just gives you two lists back, without external curly braces grouping them into a single list. In Perl, you can do something like:
($var1, $var2) = process_that_returns_two_values;
But I don't think "set" allows that in Tcl.
Related
Supposing I have a TCL API like :
namespaceXY::apiXY <value> -opt1 <value1> -opt2 <value2> -opt3 <value3>
This API is used (or maybe not) in a test suite (i.e thousands of tests).
How I can check if my API have been called + tested exhaustively (all options have been called/tested).
Many thanks
You can set an execution trace on the command. That way the signature of your command won't change. So you still get the same results if any code does info args namespaceXY::apiXY. Also error messages are not affected.
proc cmdtracer {cmd op} {
global cmdtracer
dict incr cmdtracer $cmd
}
trace add execution namespaceXY::apiXY enter cmdtracer
In the end you'll have a cmdtracer dict that contains the counts of each way the command was called. You will have to figure out yourself how to check if all options have been tested. There is not enough information in your question to provide suggestions for that part.
See #SchelteBron's answer for covering commands.
Exhaustively testing all options is going to be tricky, since they could potentially all interact in complex ways and some may be mutually-exclusive (think about the standard Tcl lsearch command for example). However, auditing that all options are at least called in your own commands can be done by additional audit-only probes. Checking all the sensible combinations of them is a manual task; you probably need a coverage tool for that.
Auditing Options in C Commands
Assuming that you're dealing with the case where you've got a C command that uses Tcl_GetIndexFromObj() to parse the option name (this is common and recommended) and where you don't mind having a threading hazard (also pretty common) the idea is simple. Make an integer variable (probably with file scope) in your C code, bind it to a Tcl variable with Tcl_LinkVar(), then use the resulting index from your (successful) Tcl_GetIndexFromObj() call to set a bit in that integer variable that says that the option was parsed.
#ifdef AUDIT_OPTIONS
static int foobar_optionTracker;
#endif
// in the implementation function, called FoobarImpl here for sake of argument
int index;
if (Tcl_GetIndexFromObj(interp, objPtr, optionNameTable, "option", 0, &index) != TCL_OK) {
return TCL_ERROR;
}
#ifdef AUDIT_OPTIONS
foobar_optionTracker |= 1 << index;
// Theoretically should call Tcl_UpdateLinkedVar() here, but for audit-only its not important
#endif
switch (index) {
// ...
}
// In your command registration function
Tcl_CreateObjCommand(interp, "foobar", FoobarImpl, NULL, NULL);
#ifdef AUDIT_OPTIONS
Tcl_LinkVar(interp, "optionTracker(foobar)", (void*) &foobar_optionTracker, TCL_LINK_INT);
#endif
With that in place, you can just read the array element optionTracker(foobar) from your Tcl test control code to see what options have been parsed (assuming you're happy with a bit-mask) in the foobar command since the last time the mask was reset. You reset the mask by just writing 0 to it.
Note that there's also Tcl_GetIndexFromObjStruct() in the C API, but auditing coverage of that is not significantly different from above.
Auditing Options in Tcl Commands
The equivalent of Tcl_GetIndexFromObj() in pure Tcl code is tcl::prefix match, but that doesn't return an index. Instead it returns the full option name that you can use with switch. Auditing that is most easily done with a full array. (This is morally the same as what the version for the C code does, but adapted to work with the optimal tools in a particular language.)
proc foobar {mandatoryArgument1 mandatoryArgument2 args} {
# Parse other things here, set up the TABLE of option descriptors, etc.
foreach option $args {
set option [tcl::prefix match $TABLE $option]
if {$::DoAudit} {
set ::foobarAudit($option) 1
}
switch -- $option {
# etc...
}
}
You can use things like array size foobarAudit to count the number of options actually used, or parray foobarAudit to print out what was actually used.
I'm very new to this language and need some help. I want pass/send email variable and see that if it is present in a database table?
'proc: TEST::TestingFunc {ArrayName}{
upvar #0 $ArrayName Param
if {[info exists Param(data)]} {
set email $Param(data)
}
}'
This is not an answer, I just need the formatting capabilities to write this comment. It's not quite clear what you are asking about or what you want to do. The code you posted seems mostly OK if you want to check if there is a member named data in a given associative array, and get its value. There are a couple of errors in the code: it should look something like this
proc TEST::TestingFunc {ArrayName} {
upvar #0 $ArrayName Param
if {[info exists Param(data)]} {
set email $Param(data)
}
}
For this to work, you also need to have created a namespace called TEST.
Beyond this, I'm unsure what you want to do. What do you mean by "pass/send"? Is the "database table" you are talking about the associative array, or do you mean something else?
I am very new to tcl. In my implementation, my input is a nested-json file. I need to store the values along with the keys in a txt file.
I converted the json file to dict and then by using jsonget from http://wiki.tcl.tk/13419, I could provide the keys and found out the values of each key iteratively very easily.
Now, my problem is, my json file might get updated later. Hence, I will not know which key has been added in the json, thus I will not be able to get the values too.
My code must be generic and must apply to all these dynamic json files and it must be able to give all these keys and along with their values.
For example
{"test":[{"a":1,"b":2}]} }
result:
a 1
b 2
{"test":[{"a":1,"b":2,"c":3}]}
result:
a 1
b 2
c 3
{"test":[{"a":1,"b":2,"c":[{"d":4,"e":5}]]}
result:
a 1
b 2
c d 4
e 5
All this, without requiring any change in the code. Is there a way to do this?
Neither your example, nor your wording ("requirement") are particularly helpful. I can only second-guess on your intentions: Do you want to know how to write a piece of Tcl that processes selected content of the JSON document in a generic (regular) manner, repeatedly, without knowledge about its structure or actual literal content (e.g., key labels)?
If yes, ...
... does this help?
proc foo {json} {
foreach k [dict keys [lindex [dict get $json test] 0]] {
puts "$k => [jsonget $json test 0 $k]"
}
}
foo [json::json2dict { {"test":[{"a":1,"b":2}]} }];
foo [json::json2dict { {"test":[{"a":1,"b":2,"c":3}]} }];
foo [json::json2dict { {"test":[{"a":1 }]}}];
It does not add much to what glenn has pointed you to.
If no, ...
... refine your example, wording.
General recommendation
For serious JSON-based development, consider rl_json or tdom.
I am trying to compare using if condition
xorg != "t8405" or "t9405" or "t7805" or "t8605" or "t8705"
I want to compare if xorg is not equal to all of these values on the right side then perform Y.
I am trying to figure out how can I have more smart comparison better or shell I compare xorg with one by one value?
Regards
I think the in and ni (not in) operators are what you should look at. They test for membership (or non-membership) of a list. In this case:
if {$xorg ni {"t8405" "t9405" "t7805" "t8605" "t8705"}} {
puts "it wasn't in there!"
}
If you've got a lot of these things and are testing frequently, you're actually better off putting the values into the keys of an array and using info exists:
foreach key {"t8405" "t9405" "t7805" "t8605" "t8705"} {
set ary($key) 1
}
if {![info exists ary($xorg)]} {
puts "it wasn't in there!"
}
It takes more setup doing it this way, but it's actually faster per test after that (especially from 8.5 onwards). The speedup is because arrays are internally implemented using fast hash tables; hash lookups are quicker than linear table scans. You can also use dictionaries (approximately dict set instead of set and dict exists instead of info exists) but the speed is similar.
The final option is to use lsearch -sorted if you put that list of things in order, since that switches from linear scanning to binary search. This can also be very quick and has potentially no setup cost (if you store the list sorted in the first place) but it's the option that is least clear in my experience. (The in operator uses a very simplified lsearch internally, but just in linear-scanning mode.)
# Note; I've pre-sorted this list
set items {"t7805" "t8405" "t8605" "t8705" "t9405"}
if {[lsearch -sorted -exact $items $xorg] < 0} {
puts "it wasn't in there!"
}
I usually use either the membership operators (because they're easy) or info exists if I've got a convenient set of array keys. I often have the latter around in practice...
I'm trying to get the max indexes by row from a matrix. To do this, I'm doing:
[throwaway, indexes] = max(blah, [], 2);
The variable "throwaway," would store values I don't want anymore and would never use, and I don't want to waste memory on it. Is there some way to indicate I don't want anything put into that throwaway variable? Something like undef in Perl, perhaps?
Yes, you can throw away return arguments with ~ for examples if func returns two arguments only v will be available after the following [~, v] = func()