【问题标题】:Why doesn't this deref of a reference work as a one-liner?为什么这个引用的 deref 不能作为单线工作?
【发布时间】:2020-01-14 18:57:15
【问题描述】:

给定:

my @list1 = ('a');
my @list2 = ('b');
my @list0 = ( \@list1, \@list2 );

然后

my @listRef = $list0[1]; 
my @list    = @$listRef;    # works

但是

my @list    = @$($list0[1]); # gives an error message

我不知道为什么。我错过了什么?

【问题讨论】:

  • 可以分享一下错误信息吗?
  • 你能先纠正语法错误吗,第一行,$@$listRef ...?这样我们就知道我们正在处理的是什么。这是不正确的,因为它是
  • ./x.py 第 11 行,“@$(”附近的语法错误
  • 感谢指正,但@$@... 仍然是非法语法(因此@$@listRef 无法编译)。而且我猜不出你的意思是什么......?
  • 语法更正。

标签: perl


【解决方案1】:

有一个简单的取消引用规则涵盖了这一点。简单地说:

印记后面的内容需要是它的正确引用,或者是对其进行评估的块。

具体案例from perlreftut

您始终可以使用大括号中的数组引用来代替数组的名称。 [...]

在你的情况下,它应该是

my @list = @{ $list0[1] };

(不是索引[2],因为您的@list0 有两个元素)空格只是为了便于阅读。

尝试的@$($list0[2]) 是一个语法错误,首先是因为((跟在$ 后面)不允许在标识符(变量名)中出现,大概跟在$ 后面。

{} 块在 $ 之后是允许的,并且会被评估,并且在这种情况下应该产生一个 标量 引用,被前面的 $ 取消引用;但是第一个@ 会出错。如果被推,这会变得混乱。虽然确切的规则(仍然)有点模糊,请参阅标识符解析in perldata。

前面的@$listRef 通常是正确的语法。但它引用了一个标量变量$listRef(最好是一个数组引用,因为它在那里被取消引用到一个数组中),并且示例中没有这样的东西——你有一个数组变量@listRef。

因此,如果use strict; 生效,这也将无法编译。


请注意,取消引用 arrayref 以分配新数组是昂贵的,因为它必须复制所有元素(并构造新的数组变量),而很少需要它(除非你真的想要一个副本)。有了手头上的数组引用 ($ar),您可能需要的所有东西都一应俱全,

@$ar;            # list of elements
$ar->[$index];   # specific element
@$ar[@indices];  # slice -- list of some elements, like @$ar[0,2..5,-1]
$ar->@[0,-1];    # slice, with new "postfix dereferencing" (stable at v5.24)
$#$ar;           # last index in the anonymous array referred by $ar

参见切片 in perldata 和后缀引用切片 in perlref

【讨论】:

  • '@$@' 的意思是我对编写正确的语法感到不舒服,并决定做错事。快速的手指,缓慢的头脑,在实物上会犯很多错误。对不起。我的打字超出了我的想法,感谢您提供正确的语法。 Gad,我真正的问题是三个列表,我只是不假思索地把实际的索引拿过来。
  • @ArthurSchwarez 好的,知道了,感谢您的解释。我调整了这个答案(并做了一些更小的澄清)
【解决方案2】:

你需要

@{ $list0[1] }

只要您可以使用变量的名称,您就可以使用评估为引用的块。这意味着获取数组元素的语法是

@NAME    # If you have the name
@BLOCK   # If you have a reference

也就是说

my @array1 = 4..5;
my @array2 = @array1;

和

my $array1 = [ 4..5 ];
my @array2 = @{ $array1 }

是等价的。

当块中唯一的东西是一个简单的标量($NAME 或$BLOCK)时,您可以省略花括号。这意味着

@{ $array1 }

等价于

@$array1

这就是 @$listRef 有效的原因,也是 @{ $list0[1] } 无法简化的原因。


见Perl Dereferencing Syntax。

【讨论】:

    【解决方案3】:

    你有很多事情要做,并且有多个级别的无意引用,所以让我们来看看吧:

    首先,您首先制作一个包含两个项目的列表,每个项目都是一个数组引用。你把它存储在一个数组中:

    my @list0 = ( \@list2, \@list2 );
    

    然后你请求索引为 2 的项目,它是一个项目,并将其存储在一个数组中:

    my @listRef = $list0[2];
    

    但是,没有索引为 2 的项,因为 Perl 从零开始索引。 @listRef 中的值未定义。不仅如此,您还要求提供单个项目并将其存储在数组而不是标量中。这可能不是你的意思。

    您说下面这行有效,但我认为您不知道这一点,因为即使您没有收到错误,它也不会给您带来您期望的价值。其他事情正在发生。你还没有声明或使用变量$listRef,所以Perl 为你创建了它并给它赋值undef。当您尝试取消引用它时,Perl 使用“自动激活”来创建引用。如果您从undef 开始,Perl 会在这个过程中帮助您创建参考结构:

    my @list    = @$listRef;    # works
    

    该数组中没有任何内容,因此 @list 应该为空。

    修复它以获取索引为 1 的最后一项,并修复它,以便将单个值(引用)分配给标量变量:

    my $listRef = $list0[1];
    

    Data::Dumper 在这里很方便:

    use Data::Dumper;
    
    my @list2 = qw(a b c);
    my @list0 = ( \@list2, \@list2 );
    
    my $listRef = $list0[1];
    
    print Dumper($listRef);
    

    你得到输出:

    $VAR1 = [
              'a',
              'b',
              'c'
            ];
    

    Perl 有一些特性可以捕捉这些变量命名错误,并帮助您找出问题所在。将这些添加到程序的顶部:

    use strict;
    use warnings;
    

    对于其余部分,您可能想查看我的书 Intermediate Perl,它解释了所有这些参考资料。

    而且,最近的 Perls 有一个名为 postfix dereferencing 的新功能,允许您从左到右编写取消引用:

    my @items = ( \@list2, \@list2 );
    
    my @items_of_last_ref = $items[1]->@*;
    

    【讨论】:

      【解决方案4】:

      问题不完整,也不清楚预期的结果。

      OP 尝试访问数组 @list0 中不存在的元素 $list0[2] -- 数组中的元素索引为 0 和 1。

      也许@listRef 应该是$listRef 而不是在帖子中。

      下面是我对所描述问题的看法

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      use feature 'say';
      
      my @list1 = qw/word1 word2 word3 word4/;
      my @list2 = 1000..1004;
      
      my @list0 = (\@list1, \@list2);
      
      my $ref_array = $list0[0];
      
      map{ say } @{$ref_array};
      
      $ref_array = $list0[1];
      
      map{ say } @{$ref_array};
      
      say "Element: " . @{$ref_array}[2];
      

      输出

      word1
      word2
      word3
      word4
      1000
      1001
      1002
      1003
      1004
      Element: 1002
      

      【讨论】:

        【解决方案5】:
        my @list    = @$@listRef;    # works
        

        我怀疑这行得通。这可能不会引发语法错误,但它肯定不会像你认为的那样做。一次

        my @list0 = ( \@list2, \@list2 );
        

        定义一个包含 2 个元素的数组,然后你可以访问

        my @listRef = $list0[2];
        

        第三个元素。所以@listRef 是一个包含一个元素undef 的数组。下面的代码也没有意义。

        除非问题纯粹是学术性的(zdim 已经回答了),否则我假设您希望将 @list 的第二个元素放入一个单独的数组中,我会写

        my @list = @{ $list0[1] };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-04-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多