简体   繁体   English

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

[英]How do I pass a hash to a function in Perl?

I have a function that takes a variable and an associative array, but I can't seem to get them to pass right.我有一个函数需要一个变量和一个关联数组,但我似乎无法让它们正确通过。 I think this has something to do with function declarations, however I can't figure out how they work in Perl.我认为这与函数声明有关,但是我无法弄清楚它们在 Perl 中是如何工作的。 Is there a good reference for this and how do I accomplish what I need?对此是否有很好的参考,我如何完成我需要的工作?

I should add that it needs to be passed by reference.我应该补充一点,它需要通过引用传递。

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

Pass the reference instead of the hash itself.传递引用而不是散列本身。 As in如在

PrintAA("abc", \%fooHash);

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

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

See also perlfaq7: How can I pass/return a {Function, FileHandle, Array, Hash, Method, Regex}?另请参阅 perlfaq7: 如何传递/返回 {Function, FileHandle, Array, Hash, Method, Regex}?

This code works:此代码有效:

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

The key point is the use of the array context in the my() 'statement' in the function.关键点是在函数的 my() '语句' 中使用数组上下文。


What does the array context business actually do?数组上下文业务实际上是做什么的?

Succinctly, it makes it work correctly.简而言之,它使它正常工作。

It means that the first value in the @_ array of arguments is assigned to $test , and the remaining items are assigned to the hash %aa .这意味着@_参数数组中的第一个值分配给$test ,其余项分配给哈希%aa Given the way I called it, there is an odd number of items in the @_ , so once the first item is assigned to $test , there is an even number of items available to assign to %aa , with the first item of each pair being the key ('aaa', 'bbb', 'ccc' in my example), and the second being the corresponding value.鉴于我的调用方式, @_有奇数个项目,因此一旦将第一个项目分配给$test ,就有偶数个项目可分配给%aa ,每个项目的第一个项目pair 是键(在我的示例中为 'aaa'、'bbb'、'ccc'),第二个是相应的值。

It would be possible to replace %aa with @aa , in which case, the array would have 6 items in it.可以用@aa替换%aa ,在这种情况下,数组中将有 6 个项目。 It would also be possible to replace %aa with $aa , and in that case, the variable $aa would contain the value 'aaa', and the remaining values in @_ would be ignored by the assignment.也可以用$aa替换%aa ,在这种情况下,变量$aa将包含值 'aaa',@ @_的其余值将被赋值忽略。

If you omit the parentheses around the variable list, Perl refuses to compile the code.如果省略变量列表周围的括号,Perl 将拒绝编译代码。 One of the alternative answers showed the notation:替代答案之一显示了符号:

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

This is pretty much equivalent to what I wrote;这和我写的差不多; the difference is that after the two my statements, @_ only contains 6 elements in this variation, whereas in the single my version, it still contains 7 elements.不同的是,在两个my语句之后,@ @_在这个变体中只包含 6 个元素,而在单个my版本中,它仍然包含 7 个元素。

There are definitely other questions in SO about array context. SO中肯定还有其他关于数组上下文的问题。


Actually, I wasn't asking about the my($test, %aa) = @_;实际上,我不是在问my($test, %aa) = @_; I was asking about my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \\&PrintAA );我在问my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \\&PrintAA ); versus my %hash = { 'aaa' => 1, ... };my %hash = { 'aaa' => 1, ... };

The difference is that the { ... } notation generates a hash ref and the ( ... ) notation generates a list, which maps to a hash (as opposed to hash ref).不同之处在于 { ... } 表示法生成一个散列引用,而 ( ... ) 表示法生成一个列表,该列表映射到一个散列(与散列引用相反)。 Similarly, [ ... ] generates an array ref and not an array.类似地, [ ... ] 生成一个数组 ref 而不是一个数组。

Indeed, change the 'main' code so it reads: my(%hash) = { ... };实际上,更改“主要”代码,使其显示为: my(%hash) = { ... }; and you get a run-time (but not compile time) error - treat line numbers with caution since I've added alternative codings to my file:并且您会收到运行时(但不是编译时)错误 - 请谨慎对待行号,因为我已将替代编码添加到我的文件中:

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.

Alternatively:或者:

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

The thing you're fundamentally missing is that an associative array isn't a single argument (though an associative array reference is, as in Paul Tomblin's answer).您从根本上缺少的是关联数组不是单个参数(尽管关联数组引用是,如 Paul Tomblin 的回答)。

It looks like you should pass in a reference to a hash.看起来您应该传入对哈希的引用。

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

PrintAA($foo, \%bar);

The reason you can't do a你不能做的原因

my %aa = shift;

is because Perl flattens all the arguments to a subroutine into one list, @_.是因为 Perl 将子例程的所有参数扁平化到一个列表中,@_。 Every element is copied, so passing in by reference avoids those copies as well.每个元素都被复制,因此通过引用传入也避免了这些副本。

As usual there are several ways.像往常一样,有几种方法。 Here is what Perl Best Practices , that most revered of style pointers, has to say about passing parameters to functions:这是Perl Best Practices中最受尊敬的样式指针,关于将参数传递给函数的内容:

Use a hash of named arguments for any subroutine that has more than three parameters对任何具有三个以上参数的子例程使用命名参数的散列

But since you have only two, you could get away ;) with passing them directly like this:但是因为你只有两个,你可以逃脱 ;) 像这样直接传递它们:

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

func($scalar, %hash)

And function is defined like this:函数定义如下:

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

    ... Do something ...
}

It could be more useful if you could show some code.如果您可以显示一些代码,它可能会更有用。

All the methods in previous answers work, but this was always the way I preferred to do things like this:以前答案中的所有方法都有效,但这始终是我喜欢做这样的事情的方式:

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

Note: I also changed your code a bit.注意:我也稍微更改了您的代码。 Perl's double-quoted strings will interpret "$test" to be the value of $test rather than the actual string '$test' , so you don't need that many . Perl的双引号字符串将解释"$test"是价值$test ,而不是实际的字符串'$test' ,所以你不需要很多做. s. s。

Also, I was wrong about how the prototypes work.此外,我对原型如何工作的看法是错误的。 To pass a hash, use this:要传递哈希,请使用以下命令:

PrintAA("test", %hash);

To print a hash reference, use this:要打印哈希引用,请使用以下命令:

PrintAA("test", %$ref_to_hash);

Of course, now you can't modify the hash referenced by $ref_to_hash because you're sending a copy, but you can modify a raw %hash because you're passing it as a reference.当然,现在您不能修改$ref_to_hash引用的散列,因为您正在发送副本,但是您可以修改原始%hash因为您将它作为引用传递。

Arguments to functions get flattened into a single array ( @_ ).函数的参数被扁平化为单个数组 ( @_ )。 So it's usually easiest to pass hashes to a function by reference.因此,通过引用将哈希值传递给函数通常是最容易的。

To create a hash :创建哈希

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

To create a reference to that hash :要创建对该哈希的引用:

my $href = \%myhash

To access that hash by reference;通过引用访问该哈希;

%$href

So in your sub:所以在你的子:

my $myhref = shift;

keys %$myhref;

All the other replies here so far seem rather complicated to me.到目前为止,这里的所有其他回复对我来说似乎都很复杂。 When I write Perl function I usually "expand" all the passed arguments in the first line of the function.当我编写 Perl 函数时,我通常在函数的第一行“扩展”所有传递的参数。

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

This is similar to other languages, where you declare functions as这类似于其他语言,您可以将函数声明为

... someFunction ( arg1, arg2, arg3 )

And if you do it that way and pass the hash as the last argument, you'll be fine without any tricks or special magic.如果你这样做,并将散列作为最后一个参数传递,你会没事的,没有任何技巧或特殊的魔法。 Eg:例如:

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

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

The output is as expected:输出如预期:

!!! Hello, World! !!!

This works because in Perl arguments are always passed as an array of scalar values and if you pass a hash, its key value/pairs are added to that array.这是有效的,因为在 Perl 中参数总是作为标量值的数组传递,如果你传递一个散列,它的键值/对被添加到该数组中。 In the sample above, the arguments passed to the function as array ( @_ ) are in fact:在上面的示例中,作为数组 ( @_ ) 传递给函数的参数实际上是:

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

and '!!!'和 '!!!' is simply assigned to %string , while %hash "swallows" all the other arguments, always interpreting one as a key and the next one as value (until all elements are used up).简单地分配给%string ,而%hash “吞下”所有其他参数,总是将一个解释为键,将下一个解释为值(直到所有元素都用完)。

You cannot pass multiple hashes that way and the hash cannot be the first argument, as otherwise it would swallow everything and leave all other arguments unassigned.您不能以这种方式传递多个散列,并且散列不能作为第一个参数,否则它会吞下所有内容并使所有其他参数未分配。

Of course, exactly the same works for array as a last argument.当然,对于数组作为最后一个参数的工作方式完全相同。 The only difference here is that arrays don't distinguish between keys and values.这里唯一的区别是数组不区分键和值。 For them, all arguments left over are values and just get pushed to the array.对他们来说,剩下的所有参数都是值,只是被推送到数组中。

Use the following sub to get the hash or hashref - whatever is passed :)使用以下子获取哈希或哈希引用 - 无论传递的是什么:)

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

}

Call your function like this:像这样调用你的函数:

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

Or like this (no matter):或者像这样(不管):

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

Or even like this (no matter):或者甚至像这样(不管):

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

PrintAA("test", %hash);

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

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