繁体   English   中英

Perl 中的二维数组访问

[英]Two-dimensional array access in Perl

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

  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 处的元素

我在正确的轨道上吗?

我的第一个建议是你应该use strict; use warnings; 在脚本的顶部。 这通常揭示了很多事情。

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

就像你提到的,获得数组长度的最好/最清晰的方法是my $arraySize = scalar @{$_[1]}; .


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

您可以正常声明数组

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

为了使引用更容易理解,我更喜欢->语法而不是 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并将它也分配给了一个变量。 突然间,很明显我想要摆脱它。

但是,我看不到您在哪里使用getline获得的$line 我错过了什么?

顺便说一句,我也可以在不引用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;
    }
}

看看->语法如何让我们很容易看出$cities是指向数组的引用? ${$cities}[$city_number]更清晰、更容易理解。

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

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

它可能想成为

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

修复 arraySize 之后。

如果结果不知何故是指向数组的指针,则

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

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

我变了

my $arraySize = scalar $_[1];

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

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

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

参考文献也让我困惑! 我总是喜欢尽快取消引用它们。 这对我有用:

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;
    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM