Perl 如何将哈希传递给子例程?

Perl 如何将哈希传递给子例程?,perl,hash,subroutine,Perl,Hash,Subroutine,我需要你帮我弄清楚怎么做。我的代码: my %hash; $hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',}; $hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',}; $hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',}; &printInfo(%hash); sub printInfo{ my

我需要你帮我弄清楚怎么做。我的代码:

my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};


&printInfo(%hash);

sub printInfo{
   my (%hash) = %_;
   foreach my $key (keys %_{       
    my $a = $_{$key}{'Make'};   
    my $b = $_{$key}{'Color'};   
    print "$a $b\n";
    }
}

你非常非常接近。没有用于传递哈希的
%\uu
,它必须在
@
中传递。幸运的是,哈希是使用列表上下文分配的,所以

sub printInfo {
   my %hash = @_;
   ...
}
会成功的

另外请注意,在大多数情况下,在子例程调用之前使用
&
是不必要的,因为至少是Perl 5.000。您可以像现在其他语言一样调用Perl子例程,只需名称和参数。(正如@mob在评论中指出的,在某些情况下,这仍然是必要的;如果有兴趣,请参阅以进一步了解这一点。)

我相信您希望

my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};

printInfo(%hash);

sub printInfo{
   my %hash = @_;
   foreach my $key (keys %hash){       
    my $a = $hash{$key}{'Make'};   
    my $b = $hash{$key}{'Color'};   
    print "$a $b\n";
   }
}
printInfo(%hash)
行中,将
%hash
扩展为具有交替键值对的列表


printInfo
中,
@
就是这个列表,分配给
%hash
它会从列表中的交替元素中再次创建具有相应值的键。

简单的方法是分配默认数组,在代码演化时可能会导致问题(包含所有键-值对作为偶数列表)添加到%hash,然后重新生成相应的值。因此,您的代码如下所示:

sub printInfo {
   my %hash = @_;
   ...
}
sub do_hash_thing { 
    my $hash_ref = shift;
    ...
}

do_hash_thing( \%hash, @other_args );
更好的方法是将哈希作为引用传递给子例程。这样,您仍然可以将更多参数传递给子例程

printInfo(\%hash);
sub PrintInfo {
   my %hash = %{$_[0]};
   ...
}
有关在Perl中使用引用的介绍,请参见

  • 参数列表
    do\u hash\u thing(%hash)
  • 对参数列表中哈希的引用 `做杂凑(之前@args\u、\%hash、@args\u之后)
  • 作为原型的参考,类似于
    和其他哈希运算符
  • 该列表的工作原理如下:

    sub do_hash_thing {
        my %hash = @_;
        ...
    }
    
    do_hash_thing( %hash );
    
    这还允许您“流化”散列参数:

    do_hash_thing( %hash_1, %hash_2, parameter => 'green', other => 'pair' );
    
    参考如下:

    sub printInfo {
       my %hash = @_;
       ...
    }
    
    sub do_hash_thing { 
        my $hash_ref = shift;
        ...
    }
    
    do_hash_thing( \%hash, @other_args );
    
    这里使用prototype
    (\%@)
    。prototype使perl在第一个参数中查找散列并通过引用传递它

    sub do_hash_thing (\%@) { 
        my $hash_ref = shift;
        ...
    }
    
    do_hash_thing( %hash => qw(other args) );
    # OR
    do_hash_thing %hash => qw(other args);
    

    警告:原型在方法上不起作用。

    传递散列和数组的最佳方式是通过。引用只是将复杂数据结构作为单个数据点进行讨论的一种方式,它可以存储在标量变量中(如
    $foo

    请继续阅读,以便您了解如何创建引用和取消引用,以便恢复原始数据

    最基本的是:在数据结构前面加一个反斜杠以获得对该结构的引用

    my $hash_ref   = \%hash;
    my $array_ref  = \@array;
    my $scalar_ref = \$scalar;   #Legal, but doesn't do much for you...
    
    sub mysub {
        my $hash_ref = shift;
    
        if ( ref $hash_ref ne "HASH" ) {
            croak qq(You need to pass in a hash reference);
        }
    
    引用是原始结构的内存位置(加上有关该结构的线索):

    将打印如下内容:

    HASH(0x7f9b0a843708)
    
    要将引用恢复为可用格式,只需将引用放在正确的前面:

    my %new_hash = %{ $hash_ref };
    
    您应该了解如何使用引用,因为这是在Perl中创建极其复杂的数据结构的方法,以及面向对象的Perl是如何工作的


    假设要将三个哈希值传递给子例程。以下是三个哈希值:

    my %hash1 = ( this => 1, that => 2, the => 3, other => 4 );
    my %hash2 = ( tom => 10, dick => 20, harry => 30 );
    my %hash3 = ( no => 100, man => 200, is => 300, an => 400, island => 500 );
    
    我将为它们创建引用

    my $hash_ref1 = \%hash1;
    my $hash_ref2 = \%hash2;
    my $hash_ref3 = \%hash3;
    
    现在只需传递参考:

    mysub ( $hash_ref1, $hash_ref2, $hash_ref3 );
    
    引用是标量数据,因此将它们传递给我的子例程没有问题:

    sub mysub {
        my $sub_hash_ref1  = shift;
        my $sub_hash_ref2  = shift;
        my $sub_hash_ref3  = shift;
    
    现在,我只是去引用它们,我的子程序可以使用它们

        my %sub_hash1 = %{ $sub_hash_ref1 };
        my %sub_hash2 = %{ $sub_hash_ref2 };
        my %sub_hash3 = %{ $sub_hash_ref3 };
    
    您可以使用以下命令查看引用所指的内容:

    如果希望确保传递的数据结构类型正确,则此选项非常有用

    my $hash_ref   = \%hash;
    my $array_ref  = \@array;
    my $scalar_ref = \$scalar;   #Legal, but doesn't do much for you...
    
    sub mysub {
        my $hash_ref = shift;
    
        if ( ref $hash_ref ne "HASH" ) {
            croak qq(You need to pass in a hash reference);
        }
    
    还要注意,这些是内存引用,因此修改引用将修改原始哈希:

    my %hash = (this => 1, is => 2, a => 3 test => 4);
    print "$hash{test}\n";   # Printing "4" as expected
    sub mysub ( \%hash );    # Passing the reference
    print "$hash{test}\n";   # This is printing "foo". See subroutine:
    
    
    sub mysub { 
        my $hash_ref = shift;
    
        $hash_ref->{test} = "foo";    This is modifying the original hash!
    }
    

    这可能是好的——它允许您修改传递给子例程的数据,也可能是坏的——它允许您无意中修改传递给原始子例程的数据。

    &
    通常是可选的,而且(通常)由于Perl生成默认参数的方式,使用它不是一种最佳实践,但它并没有被弃用。有时它是必需的,有时它不是必需的,但它仍然可以使代码更具可读性。先生,您在技术上是正确的:最好的一种正确。我已经澄清了我对此的评论。不是“而且可能更老”。在1993-05年2月的最后一个perl4版本4.036中,函数调用通常仍然需要
    &
    。相信我,我也在场。☺ 避免这种情况的唯一方法是使用
    do mysubname()
    语法-顺便说一句,它仍然可以很好地工作,这让少数偶然发现它的人感到惊讶。mob
    &subname
    仍然用于
    定义的
    未定义的
    参数,或`\\`或
    转到
    运算符的操作数。对于调用,它用于规避原型检查。
    &foo()
    对你的
    @
    很好,而
    &foo
    没有parens是隐藏的。
    goto&foo
    对于尾部递归可能更清晰,除非你不能从这里开始。
    ()
    是偶数长度列表,
    (\%)
    是散列引用。第二个选项是最好的方法。
    printInfo(\%hash);
    sub PrintInfo {
       my %hash = %{$_[0]};
       ...
    }