如何将哈希传递给 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 代码一样将 null 传递给标准输入?