为什么 Tcl expr 将某个数字视为“空”,而它实际上并不空?
问题描述
我从一个未知的来源导入了一组坐标,而我并没有权限去查看这些坐标。
set yMin [lindex [lindex $bbox 0] 1]
puts "yMin: <$yMin>"
一切正常。
yMin: <-6.149999999999999e-02>
括号是用来检查其中是否有空格甚至是隐藏的制表符。 但是 ,如果将 yMin 乘以任意数(比如 0.5),就会出错。
set Y2 [expr {0.5 * $yMin}]
can't use empty string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within "set Y2 [expr {0.5 * $yMin}]"
即使只打印 yMin ,也会出现空操作数错误。
set Y1 [expr $yMin]
puts "Y1: <$Y1>"
empty expression in expression "" (parsing expression "")
invoked from within "expr $yMin"
invoked from within "set Y1 [expr $yMin]"
但是有一个有趣的测试。如果添加花括号,它就会起作用!
set Y1 [expr {$yMin}]
puts "Y1: <$Y1>"
Y1: <-0.06149999999999999>
如何重现问题
感谢 Glenn Jackman(见下面的回复),
% set bbox {{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}}
{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}
% set yMin [lindex [lindex $bbox 0] 1]
-6.149999999999999e-02
% expr {1 + $yMin}
0.9385
实际上,这似乎无法重现问题(这就是我写这篇文章的原因)。但它至少可以是一个模板。
反复试验
以下代码用于检查它是否真的为空。事实证明它不为空。
if {$yMin eq {}} {
puts "Empty records"
exit 1
} else {
puts "yMin is not empty: <$yMin>
}
yMin is not empty: <-6.149999999999999e-02>
最后,我尝试了 trim 、 ma​​p 和 regsub 来删除所有空格和制表符,但都不起作用。
set trim_yMin [string trim $yMin]
puts "trim_yMin: <$trim_yMin>"
set map_yMin [string map {" " ""} $yMin]
puts "map_yMin: <$map_yMin>"
regsub -all {\s} $yMin {} reg_yMin
puts "reg_yMin: <$reg_yMin>"
if {$trim_yMin eq {} || $map_yMin eq {} || $reg_yMin eq {}} {
puts "Empty records"
exit 1
} else {
puts "trim_yMin is not empty: <$reg_yMin>"
puts "map_yMin is not empty: <$reg_yMin>"
puts "reg_yMin is not empty: <$reg_yMin>"
}
trim_yMin: <-6.149999999999999e-02>
map_yMin: <-6.149999999999999e-02>
reg_yMin: <-6.149999999999999e-02>
trim_yMin is not empty: <-6.149999999999999e-02>
map_yMin is not empty: <-6.149999999999999e-02>
reg_yMin is not empty: <-6.149999999999999e-02>
这里我只显示了 regsub 的结果。其他人的结果是一样的。
set reg_Y2 [expr {0.5 * $reg_yMin}]
puts "0.5 * reg_yMin: $reg_Y2"
can't use empty string as operand of "*"
while executing "expr {0.5 * $reg_yMin}"
invoked from within "set reg_Y2 [expr {0.5 * $reg_yMin}]"
你能帮帮我吗?我真的不知道我还能尝试什么。提前致谢。
更新和回复
致 Ergwun :
puts $bbox;
set yMin [lindex [lindex $bbox 0] 1]
puts "yMin: <$yMin>"
{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}
yMin: <-6.149999999999999e-02>
set Y2 [expr {0.5 * $yMin}]
puts $Y2
can't use empty string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within "set Y2 [expr {0.5 * $yMin}]"
致 benaja :
puts [tcl::unsupported::representation $yMin]
value is a string with a refcount of 4, object pointer at 0x44ed0910, internal representation 0x450d6120:(nil), string representation "-6.14999999999...."
致 Shawn ,
puts [tcl::unsupported::representation $yMin]
puts [binary encode hex $yMin]
puts [tcl::unsupported::representation $yMin]
puts [string is double -strict $yMin]
puts [tcl::unsupported::representation $yMin]
value is a string with a refcount of 4, object pointer at 0x44ed6030, internal representation 0x450de4b0:(nil), string representation "-6.1499999999..."
2d362e313439393939393939393939393939652d3032
value is a bytearray with a refcount of 4, object pointer at 0x44ed6030, internal representation 0x44ee0dc0:(nil), string representation "-6.1499999999..."
1
value is a double with a refcount of 4, object pointer at 0x44ed6030, internal representation 0xbfaf7ced916872af:(nil), string representation "-6.1499999999..."
set Y2 [expr {0.5 * -6.149999999999999e-02}]
puts "Y2: $Y2"
set new_Y2 [expr {0.5*-6.149999999999999e-02}]
puts "new_Y2: $new_Y2"
Y2: -0.030749999999999996
new_Y2: -0.030749999999999996
致 Schelte Bron ,
错误信息已更改!也许这暗示了什么?
puts [tcl::unsupported::representation $yMin]
set yMin " $yMin"
puts [tcl::unsupported::representation $yMin]
set Y2 [expr {0.5 * $yMin}]
puts $Y2
value is a string with a refcount of 4, object pointer at 0x44ec28a0, internal representation 0x450be540:(nil), string representation "-6.1499999999..."
value is a string with a refcount of 2, object pointer at 0x446bbc70, internal representation 0x44695fd0:(nil), string representation " -6.149999999..."
can't use non-numeric string as operand of "*"
while executing "expr {0.5 * $yMin}"
invoked from within
"set Y2 [expr {0.5 * $yMin}]"
If the calculation still fails after the value has changed to a double after string is double -strict $yMin, then take the result of binary encode hex $yMin, convert it back to a string (using binary decode hex) and use that in the calculation.
puts "Is yMin double? : [string is double -strict $yMin]"
set binary_yMin [binary encode hex $yMin]
set double_binary_yMin [binary decode hex $binary_yMin]
puts "yMin: $yMin"
puts "Binary of double yMin: $binary_yMin"
puts "Double binary double: $double_binary_yMin"
set Y2 [expr {0.5 * $double_binary_yMin}]
puts $Y2
Is yMin double? : 1
yMin: -6.149999999999999e-02
Binary of double yMin: 2d362e313439393939393939393939393939652d3032
Double binary double: -6.149999999999999e-02
can't use empty string as operand of "*"
while executing "expr {0.5 * $double_binary_yMin}"
invoked from within "set Y2 [expr {0.5 * $double_binary_yMin}]"
What do you get with tcl::mathop::* 0.5 $yMin?
puts [tcl::mathop::* 0.5 $yMin]
can't use empty string as operand of "*"
while executing
"tcl::mathop::* 0.5 $yMin"
invoked from within
"puts [tcl::mathop::* 0.5 $yMin]"
相关帖子
如果
bbox
变量中没有确切的起点,我们只能进行推测。该值应该有效:
% set yMin -6.149999999999999e-02
-6.149999999999999e-02
% expr {0.5 * $yMin}
-0.030749999999999996
如果输入数据是普通的伪造数据,并且空字符串的数字值永远不足以支持乘法,那么所有寻找
expr
可能失败的方法都无济于事。(这不是 Python!)我们必须后退几步。
我
怀疑
问题在于
bbox
变量有时不是您期望的形状,有时具有如下值:
1.0 -6.1499999999999999e-02 3.0 4.0
。在这种情况下,
[lindex [lindex $bbox 0] 1]
将生成一个空字符串(因为根据语言的历史,索引列表末尾会产生这样的结果):
% set bbox {1.0 -6.149999999999999e-02 3.0 4.0}
1.0 -6.149999999999999e-02 3.0 4.0
% puts >[lindex [lindex $bbox 0] 1]<
><
在这种情况下,只使用
lindex $bbox 1
效果会更好。
% puts >[lindex $bbox 1]<
>-6.149999999999999e-02<
嵌套
lindex
有一个简写:
lindex $boox 0 1
但边界框(至少当它们来自 Tk 时)通常只是四个数值的简单列表,
X
1
Y
1
X
2
Y
2
,因此对它们使用嵌套形式很可能是错误且低效的。
这只是猜测:您指出坐标来自一些不为人知的来源。也许开发人员创建了一个新的对象类型,但没有正确实现转换过程。当您打印值时,将使用字符串表示。但是当您尝试乘以该值时,它会将神秘的对象类型转换为双精度,这似乎无法正常工作。
您一直在尝试删除空格。但由于没有空格,因此不会修改该值。但是如果您添加空格会发生什么?例如:
set yMin " $yMin"
。这应该将变量变成纯字符串(您可以使用
tcl::unsupported::representation
进行检查)。如果您尝试将其相乘,将使用将字符串转换为双精度的常规 Tcl 转换过程。请注意,数字周围的空格对于
expr
来说是完全可以接受的,因此您应该能够乘以修改后的 $yMin。
这不是答案,而是带有格式的评论
% set bbox {{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}}
{-4.599999999999999e-02 -6.149999999999999e-02} {8.000000000000002e-02 6.149999999999999e-02}
% set yMin [lindex [lindex $bbox 0] 1]
-6.149999999999999e-02
% expr {1 + $yMin}
0.9385
您的代码中发生了其他事情,正在改变变量的值。请查看 如何创建最小、可重现的示例