为啥除法被解析为正则表达式?
Posted
技术标签:
【中文标题】为啥除法被解析为正则表达式?【英文标题】:Why is division parsed as regular expression?为什么除法被解析为正则表达式? 【发布时间】:2021-10-22 02:46:43 【问题描述】:这是我的代码的一部分:
my $suma = U::round $item-> suma ; # line 36
$ts += $suma;
$tnds += U::round $suma /6;
return( $ts, $tnds );
sub create #line 46
my( $c ) = shift;
my $info = $c->req->json;
my $header = @$info[0];
my $details = @$info[1];
my $agre = D::T Agreement => $header-> agreement_id ;
my( $total_suma, $total_nds ) = total( $details );
my $saldo = 0;
my $iid = @$details[0]-> period ;
my $interval = D::T Period => $iid //7; # line 58
# This is first Invoice if operator do not provide activation date
my $is_first = !$details->[0]valid_from && $iid && $interval;
当这个模块被加载时,我会报错:
Can't load application from file "lib/MaitreD/Controller/ManualDocument.pm line 38, near "my $interval = D::T Period => $iid /"
Unknown regexp modifier "/6" at lib/MaitreD/Controller/ManualDocument.pm line 38, at end of line
Global symbol "$pkg" requires explicit package name (did you forget to declare "my $pkg"?) at lib/MaitreD/Controller/ManualDocument.pm line 41.
...
这个间接宾语叫有罪吗?
因为当我在U::round( $suma /6 )
加上括号时没有错误
【问题讨论】:
最小可重现示例:CORE::say $_ /6
;-)
虽然sub T::t 1 T::t $_ /6
可能是更好的例子。因为CORE::say $_ /6/
是有效的 Perl($_
用于文件句柄),而 T::t $_ /6/
不是。
也可以用perl -e 'print $_ /6'
重现。令人惊讶的是我以前从未注意到它。
【参考方案1】:
以下是对此的一些想法,以及一个合理的解释。一个简单的复制
perl -wE'sub tt say "@_" ; $v = 7; tt $v /3'
给我
搜索模式未在 -e 第 1 行终止。所以它会尝试解析该子例程调用中的正则表达式,如前所述,问题是:为什么?
在参数周围加上括号,它可以按预期工作。后面有更多参数,它会以同样的方式失败,但前面有参数它会起作用
perl -wE'sub tt say "@_" ; $v = 7; tt $v /3, 3' # fails the same way
perl -wE'sub tt say "@_" ; $v = 7; tt 3, $v /3' # works
为tt
sub 配备原型并不会改变这一切。
通过错误看来,/
触发了search for 结束分隔符,一旦it's not found 整个事情都失败了。那么为什么这被解释为正则表达式而不是除法呢?
似乎tt $v
在解析中被分组,并被解释为子及其参数,因为它们后面跟着一个空格;然后 /3
被单独使用,然后它看起来确实像一个正则表达式。†这仍然会作为语法错误而失败,但也许正则表达式解析失败首先出现。
那么之前或之后的其他以逗号分隔的术语之间的区别就很明显了——tt 3, ...
后面的$v /3
是下一个参数的术语,并被解析为除法。
这仍然留下另一个问题。我尝试过的所有内置函数都没有这个问题,无论是列表还是一元运算符,都有各种原型(push
、chr
、splice
等)——除了print
,它确实有同样的外观问题。无论有没有括号都会失败。
perl -wE'$v=110; say for unpack "A1A1", $v /2' #--> 5 5
perl -wE'$v=200; say chr $v /2' #--> d
perl -wE'$v=3; push @ary, $v /2; say "@ary"' #--> 1.5
perl -wE'$v = 7; say $v /3' # fails, the same way
perl -wE'$v = 7; say( $v /3 )' # fails as well, same way
不同之处在于print
遵循“特殊”解析规则,并且允许第一个参数是文件句柄。 (此外,它没有原型,但这似乎并不重要。)
那么表达式print $v /3...
确实可以解析为print filehandle EXPR
,而以/
开头的EXPR
被解析为正则表达式。括号也一样。‡
所有这些都涉及一些猜测,因为我不知道解析器是如何做到的。但这显然是如何解析子例程调用的细节问题,什么(意外?)也包括print
。
在我看来,在(用户定义的)子例程上使用括号的明显补救措施是合理的。另一个解决方法是与数学运算符周围的空格保持一致,要么在两边都没有它们,要么在两边都使用它们——这也很好,即使它很痒(空格?真的吗?)。
不过,我不知道该说什么say( $v /3 )
有问题。
关于这个问题的更多问题。
从问题中的错误消息文本Unknown regexp modifier "/6"
看来,/
似乎被视为结束分隔符,这与上面的示例不同。该消息中还有更多内容,尚不清楚。最后,我们确实有一个非常相似的解析问题。
至于
这个间接宾语叫有罪吗?
我没有看到间接的对象调用,只有普通的子程序调用。此外,此答案中的示例显示了非常相似的行为并排除了间接对象语法。
† 另一种可能性是 $v /3
被解析为一个术语,因为它遵循(可识别!)子例程名称 tt
。然后,正则表达式绑定运算符=~
binds 比除法更紧密,这里通过明确尝试默认绑定到$_
来暗示。
我发现这不太可能,而且它也无法解释内置函数的行为,尤其是print
。
‡ 然后可以推断出其他带有可选的无逗号第一个参数(因此没有原型)的内置函数会以同样的方式进行,但我想不出任何一个。
【讨论】:
请注意,如果我们在/
之后放置空格,它也可以:perl -wE'sub tt say "@_" ; $v = 7; tt $v / 3'
【参考方案2】:
Perl 认为符号/
是正则表达式的开始而不是除法运算符。 https://perldoc.perl.org/perlre - 您可以检查 perldoc 中的正则表达式。
您可以尝试在6
之前添加一个空格字符,如下所示:$tnds += U::round $suma / 6;
【讨论】:
以上是关于为啥除法被解析为正则表达式?的主要内容,如果未能解决你的问题,请参考以下文章