Perl中的二维数组访问

时间:2014-02-23 21:53:05

标签: arrays perl scalar-context

尝试学习Perl。我有一个填充了城市的阵列。我想通过引用子例程传递数组并打印每个城市输出。但是我有以下问题:

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

...
Use of uninitialized value in print at <filename> line 44, <GEN2> line 997 (#1)
Use of uninitialized value in print at <filename> line 44, <GEN2> line 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;
    }
}

2)Perl语法让我非常困惑。我不明白@ {$ _ [1]} [0]发生了什么。试图解决它。

  1. $ _ [1],将此位置的值视为标量值(内存 数组的地址)
  2. @ {...},解释存储在此处的内容 内存地址作为数组
  3. @ {...} [x],访问索引x
  4. 的元素

    我是在正确的轨道上吗?

5 个答案:

答案 0 :(得分:5)

我的第一个提示是,您应该将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

答案 1 :(得分:3)

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

但是,我无法看到您使用$line获得的getlinewhile。我错过了什么?

顺便说一句,我本可以在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]是指向数组的引用?比{{1}}更清晰,更容易理解。

答案 2 :(得分:1)

此代码实际上不会编译。

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

可能想成为:

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

如果结果以某种方式指向数组,那么

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

答案 3 :(得分:0)

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

我改变了

my $arraySize = scalar $_[1];

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

我的第二个印刷声明是按照我想要的方式打印。

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

答案 4 :(得分:0)

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

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程序员......