6

我知道我可以通过将输入记录分隔符 ( $/) 设置为未定义的值来读取文件,例如

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 上提供了带有分配的变体和没有分配的变体的示例(尽管后者并不令人惊讶,更常见)。

但我的问题是它为什么有效?分配的变体是什么?

PS:我知道我应该使用例如。File::Slurper用于 slurp 文件,但生活有时就是这样有趣。

4

1 回答 1

4

这是您不应该依赖的未记录优化的结果。


通常LHS = RHS评估如下:

  1. 评估 RHS。
  2. 评估 LHS。
  3. 评估分配。

如您所见,首先评估赋值的右侧[1]。这允许以下工作:

my $x = 123;

{
   my $x = $x * 2;
   say $x;  # 456
}

say $x;  # 123

显然,在你的案例中发生了一些不同的——而且没有记录的事情。那是因为LHS = <$fh>很特别。readline( )不是从文件中读取然后将结果分配给左侧,而是<>直接写入分配左侧的结果。[2]

  1. 评估 LHS。(这会备份$/并将其设置为undef您的情况。)
  2. $fh被评估。
  3. readline被评估,直接写入赋值左侧的结果。

不执行任何分配。

此优化未记录在案,您不应依赖它。

local $/ = uc(<$fh>)例如,行不通。


  1. 编译后的代码首先评估右侧:

    $ 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
    
  2. $x = uc(<>)评估uc(<>)before $x,然后执行赋值:

    $ 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(<>)评估$xbefore <>,并且它不执行分配:

    $ 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
    

    S请注意旁边的(大写)readline以前不存在。这个“特殊”标志告诉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
    
于 2019-09-02T14:53:51.740 回答