开发者问题收集

为什么 Tcl expr 将某个数字视为“空”,而它实际上并不空?

2021-09-16
523

问题描述

我从一个未知的来源导入了一组坐标,而我并没有权限去查看这些坐标。

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]"


相关帖子

  1. 为什么 Tcler 建议用括号括住你的 表达式
  2. 这是什么意思 - 不能在 tcl 上使用空字符串作为“*”的操作数 -?如何解决
  3. 如何在 TCL 中去除字符串中的空格?
  4. 使用 TCL 从文件读取字符串后删除空格
  5. tcl 脚本中 @ 处缺少操作数
  6. 确定 Tcl 中的变量类型
3个回答

如果 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 ,因此对它们使用嵌套形式很可能是错误且低效的。

Donal Fellows
2021-09-16

这只是猜测:您指出坐标来自一些不为人知的来源。也许开发人员创建了一个新的对象类型,但没有正确实现转换过程。当您打印值时,将使用字符串表示。但是当您尝试乘以该值时,它会将神秘的对象类型转换为双精度,这似乎无法正常工作。

您一直在尝试删除空格。但由于没有空格,因此不会修改该值。但是如果您添加空格会发生什么?例如: set yMin " $yMin" 。这应该将变量变成纯字符串(您可以使用 tcl::unsupported::representation 进行检查)。如果您尝试将其相乘,将使用将字符串转换为双精度的常规 Tcl 转换过程。请注意,数字周围的空格对于 expr 来说是完全可以接受的,因此您应该能够乘以修改后的 $yMin。

Schelte Bron
2021-09-16

这不是答案,而是带有格式的评论

% 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

您的代码中发生了其他事情,正在改变变量的值。请查看 如何创建最小、可重现的示例

2021-09-16