list doesn't contain its own members in Tcl - tcl

I have a list containing one member, that member is the string <cmd_stichstudy1>XXDDR0_MA[12]. When I search for that string in the list (using lsearch) I get that the list doesn't contain it. I even get it when I search for the member of the list:
tcl> set nets_names
{<cmd_stichstudy1>XXDDR0_MA[12]}
tcl> lsearch $nets_names [lindex $nets_names 0]
-1
Why does this happen?

If you use -exact it will work the way you want.
% set nets_names {<cmd_stichstudy1>XXDDR0_MA[12]}
<cmd_stichstudy1>XXDDR0_MA[12]
% lsearch -exact $nets_names [lindex $nets_names 0]
0
%

lsearch has an unfortunate property of using glob-style matching by default.
To cite the manual:
If all matching style options are omitted, the default matching style is -glob.
So always pass -exact to lsearch unless you really want -glob.

Related

how to use lsearch in tcl with numeric

I am new to TCL word. I have a list which js numeric and when I use lsearch for numeric it is not print properly. Could you please help me what's wrong in my command
set a {12,121,124,21,212}
lsearch -integer $a 12
Expected output : 12
Actual Output : 12,121, 124, 212
You've got a list that is separated by commas, whereas Tcl lists (the kind that lsearch can search and lsort can sort) are separated by spaces. The split command can do the conversion for you:
set a {12,121,124,21,212}
set theList [split $a ","]
lsearch -integer $theList 12
The result of the search is 0, which is the index of the first item in the list (Tcl uses zero-indexing, like a lot of programming languages).
To get the actual value found (no so useful in this case, but definitely more useful in more complex ones) you'd provide the -inline option.
lsearch -inline -integer $theList 12

How to search for 0,a1[4],* where * is a wildcard in a list of 0,a2,4 0,a1[4],3 0,a1[4],5 .... in tcl

I tried lsearch -all $list_ 0,a1[4],*
a1[4] is stored in a variable
SO basically need
set var "a1[4]"
lsearch -all $list_ 0,$var,*
By default lsearch uses glob patterns (as described by the documentation for string match — it's the exact same matching engine being used). That's good because it means that * is a wildcard, but awkward because it means that [ is also special (it starts a character set match). You need some simple escaping, and to keep that sane you should put your whole pattern in {braces} so we don't need to fight with Tcl over what the meanings of bracket and backslash are:
lsearch -all $list_ {0,a1\[4\],*}
You don't need braces; you could write this instead:
lsearch -all $list_ 0,a1\\\[4\\\],*
But that's ugly! And difficult to maintain (trust me on that). So use braces, OK?
In the case where you're pulling the subpattern from a variable, things get more complicated. The fix is to use string map (or regsub) to condition the pattern piece.
# Split into three lines for clarity; qvar = “quoted var”
set ADD_BACKSLASHES {[ {\[} ] {\]}}
set qvar [string map $ADD_BACKSLASHES $var]
lsearch -all $list_ 0,$qvar,*

how to filter hierarchical_names having only n level of hierarchy from the list of hierarchical_names using lsearch and regexp

hierarchy_names = a/b/c a x d/e f/g h/i/j/k l/m/n o/p
I am trying to filter out 2 level hierarchy_names ie d/e f/g and o/p from the list of hierarchy_names having multiple levels of hierarchy_names.
I tried lsearch but the problem is, it returns the matches having hierarchy level greater or equal to 2 ie a/b/c d/e f/g h/i/j/k l/m/n o/p but not exactly equals to 2 ie d/e f/g o/p due to its algorithm of returning element if it contains pattern.
I also tried regexp but the problem with it is, it returns 2 level hierarchy_names along with partial 2 level hierarchy present in higher level hierarchy_names ie a/b d/e f/g h/I j/k l/m
set hier {a/b/c a x d/e f/g h/i/j/k l/m/n}
puts [lsearch -all -inline -regexp $hier {\w+/\w+}]
puts [regexp -all -inline {\w+/\w+} $hier]
d/e f/g o/p
Line anchors might help you in this case.
% lsearch -all -inline -regexp $hier {^\w+/\w+$}
d/e f/g
I'd use a different tool in Tcl's arsenal (assuming you have Tcl 8.6).
set hierarchy_names {a/b/c a x d/e f/g h/i/j/k l/m/n o/p}
set filtered [lmap n $hierarchy_names {
if {[llength [file split $n]] != 2} continue
string cat $n
}]
puts $filtered
# d/e f/g o/p
This uses lmap to apply a short script to each element of the list. The result of the list is either a continue signal (which skips the element) or the element; the test is done by looking at the length of the output list of file split.
what about regexp?
You may devise a regular expression like this:
(?:\s+|^)(\w+/\w+)(?=\s+|$)
The first non-capturing group anchors the match pattern at the beginning of the string or a list element.
The second capturing group actually stores what you are after.
The positive lookahead makes sure that the match pattern does not become too greedy (i.e., flows into the subsequent list element).
This will return an even list of matches, with a submatch at the odd positions. To filter them out, you may use [dict values], or an explicit [foreach], e.g..
dict values [regexp -all -inline -- {(?:\s+|^)(\w+/\w+)(?=\s+|$)} $hier]

tcl lsearch can't get something like this a\[1\]

I have a lsearch issue.
here is my code.
set aa 11
lappend aa a\[1\]
lsearch $aa a\[1\]
why doesn't it work?
Try:
lsearch -exact $aa a\[1\]
By default lsearch uses glob-style matching. The backslashes prevent [1] being treated as a command substitution, but then lsearch sees the item to find as a[1] which as a glob pattern just means "a" followed by "1". So you need the -exact flag to have the item to find just treated as literal text.

regular expression to treat unbalanced braces as a word

I am getting an error message in this regex when line contains unbalanced braces.
set line "a b { c{}"
set lst [regexp -all -inline {^(\s*(\S*)\s*)*(\{(.*)\})?(\s*(\S*)\s*)*$} $line]
set lst [lindex $lst 0]
set firstelement [lindex $lst 0]
How to avoid such cases and treat unbalanced braces as a word?
When you have a string from an arbitrary source (like a user) there's no guarantee at all that it is a well-formed list. Now regexp -inline returns a list of what it matched, but the elements of that list are strings (unless you use the -indices option, of course) and that means that you can't safely use lindex on them to pick out the pieces.
The safe way to get the first “word”, assuming you define “word” to be “sequence of non-whitespace characters” (the usual user definition), is to do this:
set firstWord [lindex [regexp -all -inline {\S+} $item] 0]
It's a bit ugly, but it's totally safe. (In fact, for the first word only, use regexp -inline {\S+} $item on its own, but that won't let you get later words.)
Using split to break a string into words is also possible, but that strongly assumes that the word separator is a single (whitespace-by-default) character and does something that you might not expect if you have multi-whitespace separators, or leading and trailing whitespace. Frankly, it's more useful for dividing up non-whitespace separated strings (e.g., a file into lines, an /etc/passwd record into fields) or for turning a string into the list of its characters (with an empty second argument).
The regexp command returns a list. You then take the first element of the list. But in the final line you then treat that element as a list - but it is not guaranteed to be so - hence the actual string content matters. Instead, if you want to deal with this item as a list you need to use split and convert it into words:
% split "a b {" " "
a b \{
In your case:
set lst [lindex $lst 0]
set firstelement [lindex [split $lst " "] 0]
You may also want to look into subst. It looks like you are trying to read poorly specified tcl lists as input and doing some parsing to get them as a proper tcl list. In which case, subst -nocommands [lindex $lst 0] might be more helpful to you. For example:
% lindex [subst -nocommands [lindex $lst 0]] 2
c{}
Note that this is the content of the braced part of $line.