39

我有一个函数需要一个变量和一个关联数组,但我似乎无法让它们正确传递。我认为这与函数声明有关,但是我无法弄清楚它们在 Perl 中是如何工作的。有没有一个很好的参考,我该如何完成我需要的?

我应该补充一点,它需要通过引用传递。

sub PrintAA
{
    my $test = shift;
    my %aa   = shift;
    print $test . "\n";
    foreach (keys %aa)
    {
        print $_ . " : " . $aa{$_} . "\n";
        $aa{$_} = $aa{$_} . "+";
    }
}
4

9 回答 9

68

传递引用而不是散列本身。如在

PrintAA("abc", \%fooHash);

sub PrintAA
{
  my $test = shift;
  my $aaRef = shift;

  print $test, "\n";
  foreach (keys %{$aaRef})
  {
    print $_, " : ", $aaRef->{$_}, "\n";
  }
}

另请参阅 perlfaq7:如何传递/返回 {Function、FileHandle、Array、Hash、Method、Regex}?

于 2009-04-29T19:20:45.830 回答
17

此代码有效:

#!/bin/perl -w

use strict;

sub PrintAA
{
    my($test, %aa) = @_;
    print $test . "\n";
    foreach (keys %aa)
    {
        print $_ . " : " . $aa{$_} . "\n";
    }
}

my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );

PrintAA("test", %hash);

关键是在函数的 my() '语句' 中使用数组上下文。


数组上下文业务实际上是做什么的?

简而言之,它使它正常工作。

这意味着@_参数数组中的第一个值分配给$test,其余项分配给散列%aa。鉴于我所说的方式, 中有奇数个项目@_,所以一旦第一个项目被分配给$test,就有偶数个项目可分配给%aa,每对的第一个项目是键(' aaa', 'bbb', 'ccc' 在我的例子中),第二个是相应的值。

可以用 替换%aa@aa在这种情况下,数组中将包含 6 个项目。也可以用 替换%aa$aa在这种情况下,变量$aa将包含值“aaa”,而其余的值@_将被赋值忽略。

如果你省略了变量列表的括号,Perl 拒绝编译代码。替代答案之一显示了符号:

my $test = shift;
my(%aa) = @_;

这和我写的差不多;不同的是,在这两个my语句之后,@_这个变体中只包含了6个元素,而在单一my版本中,它仍然包含了7个元素。

SO中肯定还有其他关于数组上下文的问题。


实际上,我并没有问my($test, %aa) = @_;我问的是my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );vsmy %hash = { 'aaa' => 1, ... };

不同之处在于 { ... } 表示法生成一个哈希引用,而 ( ... ) 表示法生成一个列表,该列表映射到一个哈希(与哈希引用相反)。同样, [ ... ] 生成数组 ref 而不是数组。

实际上,更改“主”代码,使其显示为: my(%hash) = { ... }; 并且您会收到运行时(但不是编译时)错误 - 谨慎对待行号,因为我已在我的文件中添加了替代编码:

Reference found where even-sized list expected at xx.pl line 18.
...
Use of uninitialized value in concatenation (.) or string at xx.pl line 13.
于 2009-04-29T19:26:47.297 回答
12

或者:

sub PrintAA
{
    my $test       = shift;
    my %aa         = @_;
        print $test . "\n";
        foreach (keys %aa)
        {
                print $_ . " : " . $aa{$_} . "\n";
                $aa{$_} = $aa{$_} . "+";
        }
}

您从根本上缺少的是关联数组不是单个参数(尽管关联数组引用是,如 Paul Tomblin 的回答)。

于 2009-04-29T19:22:45.980 回答
4

看起来您应该传入对哈希的引用。

sub PrintAA
{
   my $test = shift;
   my $aa = shift;
   if (ref($aa) != "HASH") { die "bad arg!" }
   ....
}

PrintAA($foo, \%bar);

你不能做的原因

my %aa = shift;

是因为 Perl 将子例程的所有参数扁平化为一个列表,@_。每个元素都被复制,因此通过引用传递也可以避免这些副本。

于 2009-04-29T19:22:17.137 回答
4

像往常一样,有几种方法。以下是最受推崇的样式指针Perl Best Practices关于将参数传递给函数的内容:

对具有三个以上参数的任何子例程使用命名参数的散列

但是因为你只有两个,你可以逃脱;)像这样直接传递它们:

my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);

func($scalar, %hash)

函数定义如下:

sub func {
    my $scalar_var = shift;
    my %hash_var = @_;

    ... Do something ...
}

如果您可以显示一些代码,它可能会更有用。

于 2009-04-29T19:24:42.717 回答
3

以前答案中的所有方法都有效,但这始终是我喜欢做这样的事情的方式:

sub PrintAA ($\%)
{
    my $test       = shift;
    my %aa         = ${shift()};
    print "$test\n";
    foreach (keys %aa)
    {
        print "$_ : $aa{$_}\n";
        $aa{$_} = "$aa{$_}+";
    }
}

注意:我还稍微更改了您的代码。Perl 的双引号字符串将解释"$test"为的值$test而不是实际的 string '$test',因此您不需要那么多.s。

另外,我对原型的工作方式是错误的。要传递哈希,请使用以下命令:

PrintAA("test", %hash);

要打印哈希引用,请使用:

PrintAA("test", %$ref_to_hash);

当然,现在您不能修改引用的哈希,$ref_to_hash因为您正在发送副本,但您可以修改原始数据%hash,因为您将它作为引用传递。

于 2009-04-29T19:47:16.577 回答
2

函数的参数被扁平化为单个数组 ( @_)。因此,通过引用将哈希传递给函数通常是最简单的。

要创建哈希

my %myhash = ( key1 => "val1", key2 => "val2" );

要创建对该哈希的引用:

my $href = \%myhash

通过引用访问该哈希;

%$href

所以在你的潜艇:

my $myhref = shift;

keys %$myhref;
于 2009-04-29T19:51:04.290 回答
1

到目前为止,这里的所有其他回复对我来说似乎都相当复杂。当我编写 Perl 函数时,我通常在函数的第一行“展开”所有传递的参数。

sub someFunction {
    my ( $arg1, $arg2, $arg3 ) = @_;

这类似于其他语言,您将函数声明为

... someFunction ( arg1, arg2, arg3 )

如果你这样做并将散列作为最后一个参数传递,那么没有任何技巧或特殊魔法就可以了。例如:

sub testFunc {
    my ( $string, %hash ) = @_;
    print "$string $hash{'abc'} $hash{'efg'} $string\n";
}

my %testHash = (
    'abc' => "Hello,",
    'efg' => "World!"
);
testFunc('!!!', %testHash);

输出如预期:

!!! Hello, World! !!!

这是有效的,因为在 Perl 中,参数总是作为标量值数组传递,如果你传递一个散列,它的键值/对被添加到该数组中。在上面的示例中,作为数组 ( ) 传递给函数的参数@_实际上是:

'!!!', 'abc', 'Hello,', 'efg', 'World!'

和 '!!!' 被简单地分配给%string,而%hash“吞下”所有其他参数,总是将一个解释为键,下一个解释为值(直到所有元素都用完)。

您不能以这种方式传递多个散列,并且散列不能作为第一个参数,否则它会吞下所有内容并保留所有其他参数未分配。

当然,数组与最后一个参数完全相同。这里唯一的区别是数组不区分键和值。对他们来说,剩下的所有参数都是值,只是被推送到数组中。

于 2015-02-11T15:25:05.707 回答
0

使用以下 sub 获取哈希或 hashref - 传递的任何内容:)

sub get_args { ref( $_[0] ) ? shift() : ( @_ % 2 ) ? {} : {@_}; }
sub PrintAA
{
  my $test = shift;
  my $aa = get_args(@_);;

  # Then
  $aa->{somearg} # Do something
  $aa->{anotherearg} # Do something

}

像这样调用你的函数:

printAA($firstarg,somearg=>1, anotherarg=>2)

或者像这样(不管):

printAA($firstarg, {somearg=>1, anotherarg=>2})

甚至像这样(无论如何):

my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );

PrintAA("test", %hash);
于 2009-04-29T19:49:29.710 回答