为什么我可以通过将尖括号的结果分配给输入记录分隔符来 slurp 一个文件?
Why can I slurp a file by assigning the result of angle brackets to the input record separator?
我知道我可以通过设置 the input record separator ($/
) to an undefined value 来 slurp 一个文件,比如
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/; <$fh> };
但最近我遇到了一个非常相似但又不同的成语:
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/ = <$fh> };
(注意 local $/ = <$fh>
而不是 local $/; <$fh>
)。
这两个 工作 ,并且在 CPAN 上有两个 the variant with the assignment and the one without 的例子(尽管后者并不奇怪,更常见)。
但我的问题是为什么它有效?赋值的变体是什么?
PS:我也知道我应该使用例如。 File::Slurper 吃文件,但生活有时就是这样有趣。
这是您不应依赖的未记录优化的结果。
通常LHS = RHS
是这样计算的:
- 计算 RHS。
- 评估 LHS。
- 作业已评估。
如您所见,首先评估赋值的右侧[1]。这允许以下工作:
my $x = 123;
{
my $x = $x * 2;
say $x; # 456
}
say $x; # 123
显然,在您的案例中发生了一些不同的事情——而且没有记录在案。那是因为 LHS = <$fh>
很特别。 readline
(<>
) 不是从文件中读取然后将结果分配给左侧,而是直接写入分配左侧的结果。[ 2]
- 评估 LHS。 (这会备份
$/
并在您的情况下将其设置为 undef
。)
$fh
被评价。
readline
被评估,直接写入赋值左侧的结果。
未执行任何分配。
此优化未记录,您不应依赖它。
例如,local $/ = uc(<$fh>)
将不起作用。
编译后的代码首先计算右侧:
$ perl -MO=Concise,-exec -e'$L = $R'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*R] s <- $R
4 <#> gvsv[*L] s <- $L
5 <2> sassign vKS/2 <- =
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
下面显示了首先评估的右侧:
$ perl -e'sub f :lvalue { CORE::say $_[0]; $x } f("L") = f("R")'
R
L
$x = uc(<>)
在 $x
之前计算 uc(<>)
,然后执行赋值:
$ perl -MO=Concise,-exec -e'$x = uc(<>)'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gv[*ARGV] s \
4 <1> readline[t3] sK/1 > RHS
5 <1> uc[t4] sK/1 /
6 <#> gvsv[*x] s -> LHS
7 <2> sassign vKS/2
8 <@> leave[1 ref] vKP/REFC
-e syntax OK
$x = uc(<>)
在 <>
之前计算 $x
,并且它不执行赋值:
$ perl -MO=Concise,-exec -e'$x = <>'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*x] s -> LHS
4 <#> gv[*ARGV] s \ RHS
5 <1> readline[t3] sKS/1 /
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
请注意 readline
旁边的(大写)S
之前不存在。这个 "special" 标志告诉 readline
写入 $x
.
添加 local
不会改变任何东西。
$ perl -MO=Concise,-exec -e'local $x = <>'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <#> gvsv[*x] s/LVINTRO
4 <#> gv[*ARGV] s
5 <1> readline[t3] sKS/1
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
我知道我可以通过设置 the input record separator ($/
) to an undefined value 来 slurp 一个文件,比如
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/; <$fh> };
但最近我遇到了一个非常相似但又不同的成语:
open my $fh, '<', $filename or die "Cannot read from $file: $!";
my $contents = do { local $/ = <$fh> };
(注意 local $/ = <$fh>
而不是 local $/; <$fh>
)。
这两个 工作 ,并且在 CPAN 上有两个 the variant with the assignment and the one without 的例子(尽管后者并不奇怪,更常见)。
但我的问题是为什么它有效?赋值的变体是什么?
PS:我也知道我应该使用例如。 File::Slurper 吃文件,但生活有时就是这样有趣。
这是您不应依赖的未记录优化的结果。
通常LHS = RHS
是这样计算的:
- 计算 RHS。
- 评估 LHS。
- 作业已评估。
如您所见,首先评估赋值的右侧[1]。这允许以下工作:
my $x = 123;
{
my $x = $x * 2;
say $x; # 456
}
say $x; # 123
显然,在您的案例中发生了一些不同的事情——而且没有记录在案。那是因为 LHS = <$fh>
很特别。 readline
(<>
) 不是从文件中读取然后将结果分配给左侧,而是直接写入分配左侧的结果。[ 2]
- 评估 LHS。 (这会备份
$/
并在您的情况下将其设置为undef
。) $fh
被评价。readline
被评估,直接写入赋值左侧的结果。
未执行任何分配。
此优化未记录,您不应依赖它。
例如,local $/ = uc(<$fh>)
将不起作用。
编译后的代码首先计算右侧:
$ perl -MO=Concise,-exec -e'$L = $R' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <#> gvsv[*R] s <- $R 4 <#> gvsv[*L] s <- $L 5 <2> sassign vKS/2 <- = 6 <@> leave[1 ref] vKP/REFC -e syntax OK
下面显示了首先评估的右侧:
$ perl -e'sub f :lvalue { CORE::say $_[0]; $x } f("L") = f("R")' R L
$x = uc(<>)
在$x
之前计算uc(<>)
,然后执行赋值:$ perl -MO=Concise,-exec -e'$x = uc(<>)' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <#> gv[*ARGV] s \ 4 <1> readline[t3] sK/1 > RHS 5 <1> uc[t4] sK/1 / 6 <#> gvsv[*x] s -> LHS 7 <2> sassign vKS/2 8 <@> leave[1 ref] vKP/REFC -e syntax OK
$x = uc(<>)
在<>
之前计算$x
,并且它不执行赋值:$ perl -MO=Concise,-exec -e'$x = <>' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <#> gvsv[*x] s -> LHS 4 <#> gv[*ARGV] s \ RHS 5 <1> readline[t3] sKS/1 / 6 <@> leave[1 ref] vKP/REFC -e syntax OK
请注意
readline
旁边的(大写)S
之前不存在。这个 "special" 标志告诉readline
写入$x
.添加
local
不会改变任何东西。$ perl -MO=Concise,-exec -e'local $x = <>' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <#> gvsv[*x] s/LVINTRO 4 <#> gv[*ARGV] s 5 <1> readline[t3] sKS/1 6 <@> leave[1 ref] vKP/REFC -e syntax OK