【问题标题】:Two-dimensional array access in PerlPerl 中的二维数组访问
【发布时间】:2014-03-25 08:51:04
【问题描述】:

我有一个包含城市的数组。我想通过引用子例程来传递数组并打印每个城市以输出。但是,我有以下问题:

  1. 我可以在子例程中的 while 循环之前访问每个元素。但我无法访问我的 while 循环中的元素。我收到错误消息:

    ... 在第 44 行、第 997 行 (#1) 的打印中使用未初始化的值 在第 44 行、第 998 行打印使用未初始化的值 (#1) ...

以下是一些代码。我已经评论了打印的内容和不打印的内容(我试图删掉我的解释不需要的代码......):

@cities;

# Assume cities is loaded successfully
&loadCities(getFileHandle('cities.txt'), $NUM_CITIES, \@cities);
&printElements(getFileHandle('names.txt'), \@cities);

sub printElements{

    my $counter = 0;
    my $arraySize = scalar $_[1];

    # Prints fine!!!
    print @{$_[1][($counter)%$arraySize];

    while ((my $line = $_[0]->getline()) && $counter < 1000){

        # Doesn't print. Generates the above error
        print @{$_[1][($counter)%$arraySize];

        $counter += 1;
    }
}
  1. Perl 语法让我非常困惑。我不明白@{$_[1]}[0] 发生了什么。我正在努力解决。
  1. $_[1],将此位置的值视为标量值(内存 数组的地址)
  2. @{...},解释这里存储的内容 作为数组的内存地址
  3. @{...} [x],访问索引 x 处的元素

我走对了吗?

【问题讨论】:

    标签: arrays perl scalar-context


    【解决方案1】:

    这段代码实际上不会编译。

    print @{$_[1][($counter)%$arraySize];
    

    它可能想要成为

    print $_[1]->[($counter)%$arraySize];
    

    在你修复 arraySize 之后。

    如果结果是指向数组的指针,那么

    print "@{$_[1]->[($counter)%$arraySize]}";
    

    【讨论】:

      【解决方案2】:

      为了使引用更容易理解,我更喜欢 -&gt; 语法而不是 munge-it-all-together 语法。

      代替:

      @{$_[1]}[0].
      

      试试

      $_[1]->[0];
      

      意思是一样的。它更容易,而且看得更清楚。您可以看到 $_[1] 是一个数组引用,并且您正在引用该数组引用中的第一个元素。

      但是,更好的方法是简单地为@_ 中的各种元素设置变量。您必须再输入几个字母,但您的代码更容易理解,也更容易调试。

      sub print_elements {
          my $file_handle      = shift;   # This isn't a "reference", but an actual file handle
          my $cities_array_ref = shift;   # This is a reference to your array
      
          my @cities = @{ $cities_array_ref };  # Dereferencing makes it easier to do your program
      

      现在,您的子例程正在处理具有名称的变量,并且您的数组引用是一个使事情变得更清晰的数组。此外,您不会意外影响主程序中的值。当您使用@_ 时,它直接链接到您传递给它的值。修改@_ 会修改主程序中的值,这可能不是您想要做的。

      所以,通过你的子程序:

      sub printElements {
          my file_handle        = shift;
           my $cities_array_ref  = shift;
      
          my @cities = @{ $cities_array_ref };
          my $counter;
          my $array_size = @cities;     # No need for scalar. This is automatic
          while  ( my $line = $file_handle->getline and $counter < 1000 ) {
              chomp $line;
              my $city_number = $counter % $array_size;
              print $cities[$city_number]. "\n";
              $counter += 1;
          }
      }
      

      请注意,通过简单地分配一些变量而不是试图将所有内容塞在一起,更容易看到发生了什么。我可以很容易地看到你的子程序的参数应该是什么。如果您使用不正确的参数顺序调用子例程,您很容易发现它。另请注意,我打破了$counter % $array_size 并将其也分配给了一个变量。突然间,很明显我想要摆脱它。

      但是,我看不到您在哪里使用 $linegetline。我错过了什么?

      顺便说一句,我也可以在不引用 while 循环中的数组的情况下完成此操作:

      sub printElements {
          my file_handle        = shift;
          my $cities            = shift;   # This is an array reference!
      
          my $counter;
          my $array_size = @{ $cities };   # I need to deref to get an array
          while  ( my $line = $file_handle->getline and $counter < 1000 ) {
              chomp $line;
              my $city_number = $counter % $array_size;
              print $cities->[$city_number]. "\n";   # That's it!
              $counter += 1;
          }
      }
      

      看看-&gt; 语法如何让$cities 是指向数组的引用变得容易?比${$cities}[$city_number] 更简洁易懂。

      【讨论】:

      • 您在 printElements() 中使用的参数数量与他的示例中的 op 不同,但是 +1 是为了很好地演示使用“->”。
      • 感谢您抽出宝贵时间回复大卫
      • @jimtut 是的,当您拼写出参数时,更容易看到这样的问题......(更新答案以消除多余的参数)。
      • 回答你关于不知道我在用 $line 做什么的问题...我实际上剪掉了大量代码来尝试专注于我的问题。我不想对每个人都倾倒太多:)
      【解决方案3】:

      我的第一个提示是,您应该将use strict;use warnings; 放在脚本的顶部。这通常揭示了很多事情。

      这一行:print @{$_[1][($counter)%$arraySize]; 没有结束 }。您也不需要在$counter 周围加上括号。

      正如您提到的,获取数组长度的最佳/最清晰的方法是my $arraySize = scalar @{$_[1]};


      您可以查看文档here 以使用参考。我会给你一个快速的概述。

      你可以正常声明一个数组

      my @array = (1, 2, 3);
      

      然后你可以使用反斜杠来引用它。

      my $array_ref = \@array;
      

      如果您想使用参考,请使用@{...}。这就像使用常规数组一样。

      print @{$array_ref};
      

      您也可以将其声明为使用方括号开头的引用。

      my $array_ref = [1, 2, 3];
      print @{$array_ref}; # prints 123
      

      在 Perl 中,二维数组实际上是数组引用的数组。这是一个例子:

      my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
      print @{$array[1]}; # prints def
      

      现在让我们尝试将数组引用传递给子例程。

      my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
      
      example(\@array); # pass in an array reference
      
      sub example {
          my @arr = @{$_[0]}; # use the array reference to assign a new array
          print @{$arr[1]};
      
          print @{$_[0][1]}; # using the array reference works too!
      }
      

      现在让我们把它放在一起并打印整个二维数组。

      my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
      example(\@array);
      sub example {
          my @arr = @{$_[0]};
          for my $ref (@arr) {
              print @{$ref};
          }
      } # prints abcdefghi
      

      如果您想将这个示例用于您的 printElements 子例程,您可以很容易地调整它。


      关于在数组中打印元素的另一个注意事项。让我们从上一个示例中提取这一行:

      print @{$ref};
      

      由于我们每次循环都调用它,我们可能想在它的末尾打印一个新行。

      print @{$ref} . "\n";
      

      这打印什么?试试吧!有用吗?

      这就是内置子例程join 派上用场的地方。

      print join(" ", @{$ref}) . "\n";
      

      For 循环通常是遍历数组的最佳方式。我的回答在这里谈到了使用 while 循环来做这件事:https://stackoverflow.com/a/21950936/2534803 你也可以看看这个问题:Best way to iterate through a Perl array

      【讨论】:

      • 感谢您抽出宝贵时间回复马特
      【解决方案4】:

      引用也让我感到困惑!我总是喜欢尽快取消引用它们。这对我有用:

      sub printElements{
      
          my $counter = 0;
          my $fh = $_[0];
          my @array = @{$_[1]};
          my $arraySize = scalar @array;
      
          # Prints fine!!!
          print @array[($counter)%$arraySize];
      
          while ((my $line = $fh->getline()) && $counter < 1000){
      
              #Doesn't print. Generates the above error
              print @array[($counter)%$arraySize];
      
              $counter += 1;
          }
      }
      

      我确信其他人可以在 cmets 中解释为什么他们认为使用参考是一种更好的方法(请这样做),但在“保持简单”的口号下,我不喜欢与他们合作。可能是因为我从来都不是 C 程序员……

      【讨论】:

      • 感谢您抽出宝贵时间回答我的问题 jimtut!
      【解决方案5】:

      我想出了如何解决我的 #1 问题(如果有人可以的话,仍在寻找我的 #2 的帮助)。

      我变了

      my $arraySize = scalar $_[1];
      

      my $arraySize = @{$_[1]};
      

      我的第二个打印语句是按我想要的方式打印。

      标量 $_[1] 似乎在获取数组的内存地址,我正在对此进行修改,允许我的 $counter 超出数组中的元素数量。

      【讨论】:

      • 嘿@donsiuch 我也在寻找答案来解释一些事情。还想指出my $arraySize = @{$_[1]};my $arraySize = scalar @{$_[1]};基本相同。这里的错误是使用数组引用$_[1] 作为数组。
      猜你喜欢
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      • 2020-11-11
      • 2012-06-01
      • 1970-01-01
      • 2018-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多