如何将哈希传递给 Perl 中的函数?

Posted

技术标签:

【中文标题】如何将哈希传递给 Perl 中的函数?【英文标题】:How do I pass a hash to a function in Perl? 【发布时间】:2010-10-22 16:10:29 【问题描述】:

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

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

sub PrintAA

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

【问题讨论】:

你能告诉我们调用代码吗? 【参考方案1】:

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

PrintAA("abc", \%fooHash);

sub PrintAA

  my $test = shift;
  my $aaRef = shift;

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

另见 perlfaq7:How can I pass/return a Function, FileHandle, Array, Hash, Method, Regex?

【讨论】:

虽然不是我想的那样,但这是最简单的方法。【参考方案2】:

此代码有效:

#!/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 );my %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.

【讨论】:

TMTOWTDI,但我更喜欢这种方法。 数组上下文业务到底是做什么的? 其实我不是在问 my($test, %aa) = @_;我在问 my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );与我的 %hash = 'aaa' => 1, ... ; 请记住,如果数组很大,引用效率更高。这种调用风格将所有数组元素压入堆栈,并将它们全部弹出。引用是单个元素,与数组大小无关。【参考方案3】:

或者:

sub PrintAA

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

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

【讨论】:

【参考方案4】:

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

sub PrintAA

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


PrintAA($foo, \%bar);

你做不到的原因

my %aa = shift;

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

【讨论】:

【参考方案5】:

像往常一样,有几种方法。以下是最受推崇的样式指针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 ...

如果你能显示一些代码会更有用。

【讨论】:

【参考方案6】:

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

sub PrintAA ($\%)

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

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

另外,我对原型的工作方式有误。要传递哈希,请使用:

PrintAA("test", %hash);

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

PrintAA("test", %$ref_to_hash);

当然,现在您无法修改 $ref_to_hash 引用的哈希,因为您发送的是副本,但您可以修改原始的 %hash,因为您将其作为引用传递。

【讨论】:

投反对票,因为 1) 这段代码不起作用,2) Perl 原型并没有像大多数人(显然包括你)所期望的那样。它们非常适合模拟内置函数的行为,但没有太多用处。原型的“\%”部分表示第二个参数必须是一个散列并通过引用传递它。它必须是一个 real 散列。它不能是 hashref 或 key => value 对的列表。 感谢您的更正。 \% 不能作为参考是没有意义的,但这是真的。我认为他不会传递 key => value 对的列表,因为他的函数似乎正在修改他作为参数传入的哈希,但其他点是有效的。【参考方案7】:

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

创建一个哈希

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

创建对该散列的引用:

my $href = \%myhash

通过引用访问该哈希;

%$href

所以在你的潜艇中:

my $myhref = shift;

keys %$myhref;

【讨论】:

【参考方案8】:

到目前为止,这里的所有其他回复对我来说似乎都相当复杂。当我编写 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“吞没”所有其他参数,总是将一个解释为键,下一个解释为值(直到所有元素都用完)。

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

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

【讨论】:

【参考方案9】:

使用下面的 sub 来获取 hash 或 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);

【讨论】:

使用原型可以让你做同样的事情,以及在编译时检查你的子程序的参数。另外,你的 get_args 会被引用数组所迷惑。 @Chris - 当子例程作为方法调用时,原型没有任何作用,它们很糟糕,令人困惑并且会破坏事物。正如其他人之前发布的那样,除非您需要像内置一样制作子作品,否则不要使用它们。

以上是关于如何将哈希传递给 Perl 中的函数?的主要内容,如果未能解决你的问题,请参考以下文章

通过引用和标量变量传递 Perl 哈希

在 Perl 中如何使用变量作为哈希键?

我如何像这个 perl 代码一样将 null 传递给标准输入?

如何将两个数组分配给 Perl 中的哈希?

Perl - 我如何将完整的目录路径名传递给 perl 脚本

“在将哈希分配给 perl 中的哈希时,“不能使用字符串 (””) 作为哈希引用,而“严格引用”在使用时出现错误