Predictable order of response to Tcl array names? - tcl

I know that the names returned by [array names X] are in an undefined order, but are they always in the same undefined order?
I'm going through a very large array, and would like to log progress to a file in case of a crash, so I can resume part-way through again.
PS. A quick experiment implies it is always the same 'random' order, but this doesn't mean it's true!

The short answer is that you can't rely on the order and your best bet is to [lsort [array names X]] and use that order.
The long answer is that the order should be stable as long as the keys are the same (and its the same Tcl version)... but I still wouldn't rely on it.
If you're using Tcl 8.5 or later, you might want to look at using a Dict instead of an array. The order of elements for a Dict is the order they were added in.

Related

Please explain the expression in this Tcl if statement

I've not seen a Tcl if {expression} like the one below. I need help understanding
$TVAR(h_flows,comm,current_stage)
Thanks!!
if {$TVAR(h_flows,comm,current_stage) == "300_start"} {
puts "...
It just tests if that particular array element's value is equal to the given string (And it really should be using eq, not ==, to force string comparison).
If you're not familiar with Tcl arrays, see this introduction in the Tcler's Wiki.
The $TVAR(h_flows,comm,current_stage) is just a read (because $) from the h_flows,comm,current_stage element of the TVAR array. Yes, the name of that element has commas in it, and some people use that to create compound keys, but the array itself (and Tcl) doesn't care.

jq: groupby and nested json arrays

Let's say I have: [[1,2], [3,9], [4,2], [], []]
I would like to know the scripts to get:
The number of nested lists which are/are not non-empty. ie want to get: [3,2]
The number of nested lists which contain or not contain number 3. ie want to get: [1,4]
The number of nested lists for which the sum of the elements is/isn't less than 4. ie want to get: [3,2]
ie basic examples of nested data partition.
Since stackoverflow.com is not a coding service, I'll confine this response to the first question, with the hope that it will convince you that learning jq is worth the effort.
Let's begin by refining the question about the counts of the lists
"which are/are not empty" to emphasize that the first number in the answer should correspond to the number of empty lists (2), and the second number to the rest (3). That is, the required answer should be [2,3].
Solution using built-in filters
The next step might be to ask whether group_by can be used. If the ordering did not matter, we could simply write:
group_by(length==0) | map(length)
This returns [3,2], which is not quite what we want. It's now worth checking the documentation about what group_by is supposed to do. On checking the details at https://stedolan.github.io/jq/manual/#Builtinoperatorsandfunctions,
we see that by design group_by does indeed sort by the grouping value.
Since in jq, false < true, we could fix our first attempt by writing:
group_by(length > 0) | map(length)
That's nice, but since group_by is doing so much work when all we really need is a way to count, it's clear we should be able to come up with a more efficient (and hopefully less opaque) solution.
An efficient solution
At its core the problem boils down to counting, so let's define a generic tabulate filter for producing the counts of distinct string values. Here's a def that will suffice for present purposes:
# Produce a JSON object recording the counts of distinct
# values in the given stream, which is assumed to consist
# solely of strings.
def tabulate(stream):
reduce stream as $s ({}; .[$s] += 1);
An efficient solution can now be written down in just two lines:
tabulate(.[] | length==0 | tostring )
| [.["true", "false"]]
QED
p.s.
The function named tabulate above is sometimes called bow (for "bag of words"). In some ways, that would be a better name, especially as it would make sense to reserve the name tabulate for similar functionality that would work for arbitrary streams.

How to compare condition with multiple values in TCL

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

Is stdout necessary in tcl?

I'm very new to tcl and I hope to become proficient in this language, so I thought I should ask if there was a reason why some example codes have stdout in the code and some just use puts. The book doesn't seem to explain this. Was the IDE updated to automatically assume stdout?
The puts command writes to stdout if you don't provide it with a channel name (and has worked this way since… well, since forever). If you want, you can put it in or leave it out; it doesn't matter, but might make things clearer if done one way or the other. Let it be your choice. The only mandatory argument to puts is the string to write.
In performance terms, it's slightly faster to omit the value, but you should disregard that as the additional cost of a lookup is absolutely measly by comparison with the cost of doing IO at all. Clarity of expression is the only valid reason for picking one over the other.
There is a case where it matters though. If you want to write code that writes to standard output by default, but where you can override it to write to a file (or a socket or a pipe or …) then it's much easier to have the channel name stored in a variable (that is set to stdout by default, of course). Like that, all your code is the same except for one bit: it's not a good idea to close stdout normally. That's easy to avoid with code like this:
# Also don't want to close stdin or stderr...
if {![string match std* $channel]} {
close $channel
}

Tcl and records (structs)

Package struct::record from TCLLIB provides means for emulating record types. But record instances are commands in the current namespace and not variables in the current scope. This means there is no garbage collection for record instances. Passing name of the record instance to a procedure means passing it by reference not by value, it is possible to pass string representation of the record as parameter but it requires to create another instance in the procedure, configure it and delete by hand, it's annoying. I wonder about the rationale behind this design. A simple alternative is provide a lisp-style records - a set of construction, access and modification procedures and represent records as lists.
The struct::record implementation is, from my viewpoint, an oo-style implementation. If you're searching for a data-style implementation (like lisp) where the commands are totally separate from the data, you might want to look at the dict command.
I'll note that oo-style and data-style are really not good descriptions, but they were the best I could think of offhand.
You most certainly can do it “the Lisp way”.
proc mkFooBarRecord {foo bar} {
# Keep index #0 for a "type" for easier debugging
return [list "fooBarRecord" $foo $bar]
}
proc getFoo {fooBarRecord} {
if {[lindex $fooBarRecord 0] ne "fooBarRecord"} {error "not fooBarRecord"}
return [lindex $fooBarRecord 1]
}
# Etc.
That works quite well. (Write it in C and you can make it more efficient too.) Mind you, as a generic data structure, it seems that many people prefer Tcl 8.5's dictionaries. There are many ways to use them; here's one:
proc mkFooBarRecord {foo bar} {
return [dict create "type" fooBarRecord "foo" $foo "bar" $bar]
}
proc getFoo {fooBarRecord} {
dict with fooBarRecord {
if {$type ne "fooBarRecord"} {error "not fooBarRecord"}
return $foo
}
}
As for the whole structures versus objects debate, Tcl tends to regard objects as state with operations (leading to a natural presentation as a command, a fairly heavyweight concept) whereas structures are pure values (and so lightweight). Having written a fair chunk on this, I really don't know what's best in general; I work on a case-by-case basis. If you are going with “structures”, also consider whether you should have collections that represent fields across many structures (equivalent to using column-wise storage instead of row-wise storage in a database) as that can lead to more efficient handling in some cases.
Also consider using a database; SQLite integrates extremely well with Tcl, is reasonably efficient, and supports in-memory databases if you don't want to futz around with disk files.
I will not answer your question, because I was not been using Tcl for many years and I never use this kind of struct, but I can give you the path to two possible places that are very plausible to provide a good answer for you:
The Tcl'ers Wiki http://wiki.tcl.tk
The Frenode's Tcl IRC channel
At the time I used Tcl they proved to be invaluable resources.