TCL conditional commands using ternary operator - tcl

is it possible to run conditional commands using TCL's ternary operator?
using if statement
if {[string index $cVals $index]} {
incr As
} {
incr Bs
}
I would like to use ternary operator as follows but I get an error
invalid command name "1" while executing "[string index $cVals $index]
? incr As : incr Bs"
[string index $cVals $index] ? incr As : incr Bs

For ternary conditions, we should be using boolean values only, either 0 or 1.
So, you can't use string index directly, since it will return either a char or empty string. You have to compare whether the string is empty or not.
Also, the for the pass/fail criteria of conditions, we have to give literal values. You should use expr to evaluate the expressions.
A basic example can be ,
% expr { 0 < 1 ? "PASS" : "FAIL" }
PASS
% expr { 0 > 1 ? "PASS" : "FAIL" }
FAIL
%
Note that I have used double quotes for the string since it has the alphabets. In case of numerals, it does not have to be double quotes. Tcl will interpret numbers appropriately.
% expr { 0 > 1 ? 100 : -200 }
-200
% expr { 0 < 1 ? 100 : -200 }
100
%
Now, what can be done to your problem ?
If you want to use any commands (such as incr in your case), it should be used within square brackets, to mark that as a command.
% set cVals "Stackoverflow"
Stackoverflow
% set index 5
5
% # Index char found. So, the string is not empty.
% # Thus, the variable 'As' is created and updated with value 1
% # That is why we are getting '1' as a result.
% # Try running multiple times, you will get the updated values of 'As'
% expr {[string index $cVals $index] ne {} ? [incr As] : [incr Bs] }
1
% info exists As
1
% set As
1
% # Note that 'Bs' is not created yet...
% info exists Bs
0
%
% # Changing the index now...
% set index 100
100
% # Since the index is not available, we will get empty string.
% # So, our condition fails, thus, it will be increment 'Bs'
% expr {[string index $cVals $index] ne {} ? [incr As] : [incr Bs] }
1
% info exists Bs
1
%

Related

Numeric Value Comparison in Tcl

I want to know how to get a numeric value in TCL. What I mean is that, if the value is not numeric, result should fail else pass.
The below is what I have tried;
set trueKIND false
set trueKINDlist [list 1 2 3 4 5 6 7 8 9 10]
if {[lsearch -exact $trueKINDlist $Registrant(KIND)] >= 0} {
set trueKIND true
}
But what happens if the value of trueKINDlist > 10, this code certainly will fail...
Can somebody please tell me how I can write this in TCL? Or assist me with the operator to use in achieving this...
Thanks
Mattie
You can validate the string by [string is ...] procedure. For example:
set trueKIND [string is integer -strict $Registrant(KIND)]
Reference: https://www.tcl.tk/man/tcl8.6/TclCmd/string.htm#M10
You've got to think what kind of validation you want. For example, if you want to just validate that the value is an integer, any integer, do this:
if {![string is entier -strict $value]} {
error "$value is not an integer"
}
(There is also string is integer, but that uses a restricted 32-bit range for historical reasons, and string is wide uses a 64-bit range. For floating point numbers, use string is double. The -strict is required here; without it the empty string is also accepted; again, this is for historical reasons.)
When you have a particular range you want the value to be in, you use a compound condition:
if {![string is entier -strict $value] || !($value >= 0 && $value <= 10)} {
error "$value is not an integer in the range (0..10)"
}
If you are doing this a lot, use a procedure to make it clearer:
proc IntegerInRange {value lowerBound upperBound} {
expr {[string is entier -strict $value] && $value >= $lowerBound && $value <= $upperBound}
}
if {![IntegerInRange $value 0 10]} {
error "$value is not an integer in the range (0..10)"
}
if {![IntegerInRange $value2 3 25]} {
error "$value2 is not an integer in the range (3..25)"
}

What is the magic behind the TCL command expr with the operator eq

I would like to understand the TCL command expr for string comparison:
I tried the following:
expr {t eq t}
=> 1
expr {tru eq tr}
=> 0
expr {tru eq truee}
=> invalid bareword "truee" ...
expr {a eq a}
=> invalid bareword "a" ...
What is the magic behind the words t, tr, tru? Does Tcl special handling for these strings? I learnt that I had to quote the string if I use expr with eq, but I have some legacy programs which use this form of comparison. I would like to understand it. Thanks.
In Tcl, eq and ne does the string comparison while == does the numerical comparison.
% expr {1 == 1.0}
1
% expr {1 eq 1.0}
0
%
Whenever you are using the eq and if the input non-numeric, then it should be used with double quotes or with a variable reference. You can't use the literal bareword string notation.
For e.g
% expr {"a" eq "b"}; # Strings with double quotes
0
% set i a
a
% expr {$i eq "a"}; # String with variable reference
1
% expr {a eq b}; # Literally using the string as bareword
invalid bareword "a"
in expression "a eq b";
should be "$a" or "{a}" or "a(...)" or ...
%
There is a exception in this rule, where the Tcl boolean comes into play.
In Tcl, a proper boolean value is either a proper integer, with, like in C, zero meaning false and non-zero meaning true, or one of the following:
yes, true, on --> Boolean 1
no, false, off --> Boolean 0
When they are used partially, Tcl tends to match with any of the known items and evaluation happens accordingly.
If you evaluate expr with these special words, then it will return the same.
% expr {true}
true
% expr {false}
false
% expr {t}; # Tcl automatically matches internally and map it to 'true'
t
% expr {fa} ; # Similary, mapped to 'false'
fa
% expr {o} ; # Will throw error as it is conflicting with 'on' & 'off'
invalid bareword "o"
in expression "o";
should be "$o" or "{o}" or "o(...)" or ...
% expr {on}
on
% expr {of}; # Matching 'off' boolean
of
% if true {puts ya}
ya
% if n {puts ya}; # Matching boolean 'no'
% if f {puts ya}; # Matching boolean 'false'
% if false {puts ya}
% if yes {puts ya}
ya
% if y {puts ya}; # Matching boolean 'y'
ya
So, If your input is mapping with boolean, they are still valid, but treated as string only.
Now, lets go back to your original question.
% expr {t eq t}; # Tcl mapped the 'true' boolean and string-wise both are same. So, returned 1
1
% expr {tru eq tr}; # Both mapped 'true'. But, string-wise differs. So, returned 0
% expr {tru eq truee}; # 'tru' mapped to 'true' and 'truee' is unknown. So error
invalid bareword "truee"
% expr {a eq a}; # Both are used literally, so error throw.
invalid bareword "a"
Now, your question from the comments,
% expr {yes == true}; # String-wise, both are different. So, returned 0
0

Extra characters after close quote in conditional operator

I am using a conditional operator but getting the error "extra characters after close-quote". fdx is a parameter I am trying to pass through the function argument and I am checking if the passed argument is "fdx" or not and based on that the value to be written will be decided.
proc set_ifg_seville2 {port sgmii speed fdx} {
case $speed {
10 {
erf_wr -s dev_$port mac_ifg_cfg tx_ifg [expr ($fdx == "fdx") ? 5:4]
}
} #Closing procedure
The expression for the ternary operation in your code should be changed as,
erf_wr -s dev_$port mac_ifg_cfg tx_ifg [expr {$fdx == "fdx" ? 5:4}]
Examples :
% set fdx "fdx"
fdx
% set result [expr {$fdx=="fdx" ? "pass" : "fail" }]
pass
% set result [expr {$fdx=="stackoverflow" ? "pass" : "fail" }]
fail
%
Reference : expr

TCL incr gives wrong value for zero padded integer

I was trying to increment a number which is padded by zeroes to become a six digit number. But strangely any value other than single digit gives a wrong value. like
set x 000660
incr x 1
gives result 433. Also tried with smaller number like 010 but the result is 9. Why is this happening ?
What is the proper way to solve this issue ?
You can try this way too.
proc getIntVal { x } {
# Using 'scan' command to get the literal integer value
set count [ scan $x %d n ]
if { $count!= 1 } {
return -1
}
return $n
}
proc padZero { x } {
# Using 'format' to pad with leading zeroes.
return [ format "%05d" $x ]
}
set val 00060
puts "Initial value : $val"
set tmp [ getIntVal $val ]; # 'tmp' will have the value as '60'
incr tmp;
set val [ padZero $tmp ]; # Padding with zero now
puts "Final value : $val"
Numbers beginning with 0 like
000660
are octet integers. It's equivalent to decimal 432.
The same for 010 (the same as 8 in decimal)
To strip off zeros, try this:
proc stripzeros {value} {
regsub ^0+(.+) $value \\1 retval
return $retval
}
For more information, see Tcl FAQ: How can I use numbers with leading zeroes?.
Yu Hao already explained the problem of octets, and Dinesh added some procs to circumvent the issue. I am suggesting creating one proc that will take on a zero padded integer and return another zero padded integer of the same format and which should work just like incr:
proc incr_pad {val args} {
# Check if increment is given properly
if {[llength $args] == 0} {
set args 1
} elseif {[llength $args] > 1} {
return -code error {wrong # args: should be "incr_pad varName ?increment?"}
}
# Check for integers
if {![regexp {^[0-9]+$} $val]} {
return -code error "expected integer but got \"$val\""
} elseif {![regexp {^[0-9]+$} $args]} {
return -code error "expected integer but got \"$args\""
}
# Get number of digits
set d [regexp -all {[0-9]} $val]
# Trim 0s to the left
set newval [string trimleft $val 0]
# Now use incr
incr newval $args
# Return back the number formatted with the same zero padding as initially given
return [format "%0${d}d" $newval]
}
With this...
% incr_pad 000660 1
000661
% incr_pad 2.5 1
expected integer but got "2.5"
% incr_pad 02 1.5
expected integer but got "1.5"
% incr_pad 010 2
012
% incr_pad 1 2 3
wrong # args: should be "incr_pad varName ?increment?"
% incr_pad 00024
00025
% incr_pad 999
1000
Of course, you can change the name of the function to a shorter one or one which you find more appropriate.

Why does expr "i==i" fail with "invalid bareword"?

1)
% expr "1==1"
1
2)
% expr "i==i"
invalid bareword "i"
in expression "i==i";
should be "$i" or "{i}" or "i(...)" or ...
Why am getting this error in step - 2
1) % if {"i" == "i"} {
puts "hai"
}
hai
2) % if {i == "i"} {
puts "hai"
}
invalid bareword "i"
in expression "i == "i"";
should be "$i" or "{i}" or "i(...)" or ...
if {"i" == "i"} This is wotking with if condition .
Here I found like expr command evaluating only integers , not comparing strings , But the In "if" condition everything (integer as well as string) are evaluating .
How Things are working here ?
The answer is in the expr man page.
Operands may be specified in any of the following ways:
...
[4] As a string enclosed in double-quotes. The expression parser
will perform backslash, variable, and command substitutions on
the information between the quotes, and use the resulting value
as the operand
[5] As a string enclosed in braces. The characters between the open
brace and matching close brace will be used as the operand with‐
out any substitutions.
...
So, expr can compare strings, but you must enclose them in double quotes or curly braces, depending on whether you want substituions performed or not.
Therefore, in your example 2, you must use
% expr {"i" == "i"}
or
% expr {{i} == {i}}
Better to user the string comparison operands:
% expr {"i" eq "i"}
% expr {{i} eq {i}}
to be sure that the content of the string is not converted to numerical values.
In Tcl 8.4
you can use
%expr {"i" == "i"}
or
%expr ( "i" == "i" )
Both syntaxes will work.