print (a..c) # this prints: abc
print ($a = "abc") # this prints: abc
print ($a = a..c); # this prints: 1E0
Run Code Online (Sandbox Code Playgroud)
我本以为会打印出来:abc
use strict;
print ($a = "a".."c"); # this prints 1E0
Run Code Online (Sandbox Code Playgroud)
为什么?这只是我的电脑吗?编辑:我有一个部分答案(范围运算符..在标量上下文中返回一个布尔值 - 谢谢)但我不明白的是:为什么:print($ a ="a"..."c ")产生1而不是0为什么:print($ a ="a".."c")产生1E0而不是1或0
Mic*_*man 17
这里有许多微妙的事情.第一个是..真正的两个完全不同的运算符,具体取决于它所调用的上下文.在列表上下文中,它创建给定起点和终点之间的值列表(递增1).
@numbers = 1 .. 3; # 1, 2, 3
@letters = 'a' .. 'c'; # a, b, c (Yes, Perl can increment strings)
Run Code Online (Sandbox Code Playgroud)
因为print在列表上下文中解释其参数
print 'a' .. 'c'; # <-- this
print 'a', 'b', 'c'; # <-- is equivalent to this
Run Code Online (Sandbox Code Playgroud)
在标量上下文中,..是触发器操作符.来自perlop中的范围运算符:
只要其左操作数为假,它就是假的.一旦左操作数为真,范围运算符将保持为真,直到右操作数为真,此后范围运算符再次变为假.
分配标量值,如$a = ...创建标量上下文.这意味着..in print ($a = 'a' .. 'c')是触发器运算符的实例,而不是列表创建运算符.
触发器操作符设计用于过滤文件中的行.例如
while (<$fh>) {
print if /first/ .. /last/;
}
Run Code Online (Sandbox Code Playgroud)
将打印文件中的所有行,从包含的行开始,以包含的行first结束last.
触发器操作器具有一些额外的魔力,旨在使其易于根据行号进行过滤.
while (<$fh>) {
print if 10 .. 20;
}
Run Code Online (Sandbox Code Playgroud)
将打印文件的第10行到第20行.它通过采用特殊情况行为来做到这一点:
如果标量的任一操作数
..是常量表达式,则如果该操作数==与当前输入行号($.变量)相等(),则该操作数被视为true .
字符串a和c都是常量表达式,因此它们会触发这种特殊情况.它们不是数字,但它们被用作数字(==是数字比较).Perl将根据需要在字符串和数字之间转换标量值.在这种情况下,两个值nummify为0.因此
print ($a = 'a' .. 'c'); # <-- this
print ($a = 0 .. 0); # <-- is effectively this
print ($a = ($. == 0) .. ($. == 0)); # <-- which is really this
Run Code Online (Sandbox Code Playgroud)
我们已接近神秘的底部.到下一位.更多来自perlop:
返回的值是false的空字符串,或者是true的序列号(以1开头).为遇到的每个范围重置序列号.范围中的最终序列号附加了字符串"E0"
如果你还没有从文件中读取任何行呢,$.会undef是0在一个数值范围内.0 == 0是的,所以..返回一个真值.这是第一个真正的价值,所以它是1.因为这两个左手和右手边是真正的第一个真正的价值也是最后的真正价值和E0"这是最后的值"后缀被附加到返回值.这就是print ($a = 'a' .. 'c')打印的原因1E0.如果要设置$.为非零值,..则为false并返回空字符串.
print ($a = 'a' .. 'c'); # prints "1E0"
$. = 1;
print ($a = 'a' .. 'c'); # prints nothing
Run Code Online (Sandbox Code Playgroud)
拼图的最后一部分(我现在可能走得太远)是赋值运算符返回一个值.在这种情况下,这是分配给$a1 - 的值1E0.这个值是最终吐出来的print.
1:从技术上讲,分配为分配给的项目生成左值.即它返回变量的左值$a,然后计算结果1E0.
Kei*_*son 12
这是列表上下文与标量上下文的问题,如下所述perldoc perlop:
在标量上下文中,".."返回一个布尔值.运算符是双稳态的,就像一个触发器,并模拟sed,awk和各种编辑器的行范围(逗号)运算符.每个".."运算符都保持自己的布尔状态,甚至在调用包含它的子例程时也是如此.只要其左操作数为假,它就是假的.一旦左操作数为真,范围运算符将保持为真,直到右操作数为真,此后范围运算符再次变为假.在下次评估范围运算符之前,它不会变为假.它可以测试正确的操作数,并在相同的评估中变为false(如在awk中),但它仍然返回true一次.如果您不希望它在下一次评估之前测试正确的操作数,就像在sed中一样,只需使用三个点("...")而不是两个.在所有其他方面,"......"的行为就像"......"一样.
[剪断]
范围中的最终序列号附加了字符串"E0",它不会影响其数值,但如果要排除端点,则可以搜索一些内容.
编辑回应丹麦男子的评论:
我发现它也有点难以消化; 坦率地说,我很少使用..运算符,甚至更少使用标量上下文.但是,例如,5..10输入循环中的表达式隐式地比较当前值$.(这是我没有引用的描述的一部分;请参阅手册).在第5行到第9行,它产生一个真值(实验表明它是一个数字,但文档没有这么说).在第10行,它产生一个"E0"附加到它的数字 - 即,它是指数表示法,但它具有相同的值而没有"E0".
该点"E0"的调整是为了让你发现你是否在规定范围内是和到旗范围内的最后一行进行特殊处理.没有"E0",你将无法特别对待最后的比赛.
一个例子:
#!/usr/bin/perl
use strict;
use warnings;
while (<>) {
my $dotdot = 2..4;
print "On line $., 2..4 yields \"$dotdot\"\n";
}
Run Code Online (Sandbox Code Playgroud)
给定5行输入,打印:
On line 1, 2..4 yields ""
On line 2, 2..4 yields "1"
On line 3, 2..4 yields "2"
On line 4, 2..4 yields "3E0"
On line 5, 2..4 yields ""
Run Code Online (Sandbox Code Playgroud)
这使您可以检测线是在范围内还是在范围之外以及它是否是范围中的最后一行.
但标量..可能更常用于其布尔结果,通常是单行; 例如,perl -ne 'print if 2..4'将打印您提供的任何输入的第2,3和4行.它故意类似于sed -n '2,4p'.
Chr*_*utz 10
通过咨询perldoc的perlop页面可以找到答案:
二进制".."是范围运算符,它实际上是两个不同的运算符,具体取决于上下文.在列表上下文中,它返回一个值列表(从一个值开始计算)从左值到右值...
这是熟悉的用法,print "a" .. "c";因为函数的参数在列表上下文中进行计算,因此会调用它.(如果它们是在标量上下文中进行评估,那么print @list会打印出大小@list,这几乎绝对不是人们通常想要的.)
在标量上下文中,".."返回一个布尔值.运算符是双稳态的,就像一个触发器,并模拟sed,awk和各种编辑器的行范围(逗号)运算符.每个".."运算符都保持自己的布尔状态,甚至在调用包含它的子例程时也是如此.只要其左操作数为假,它就是假的.一旦左操作数为真,范围运算符将保持为真,直到右操作数为真,此后范围运算符再次变为假.在下次评估范围运算符之前,它不会变为假.它可以测试正确的操作数,并在相同的评估中变为false(如在awk中),但它仍然返回true一次.如果您不希望它在下一次评估之前测试正确的操作数,就像在sed中一样,只需使用三个点("...")而不是两个.在所有其他方面,"......"的行为就像"......"一样.
它进一步详细说明,但粗体部分是理解操作员如何工作的重要部分.标量上下文是强制的$a =,即赋值给标量左值.如果你这样做@a =,它会打印你所期望的.
注意,"a" .. "b"不产生字符串"abc",它产生列表("a", "b", "c").如果使用列表,则会得到类似的结果(尽管列表强制进入标量上下文时打印的值会有所不同).