由值Vs通行证一个Perl散列通过引用传递(Pass by value vs pass by ref

2019-08-17 10:54发布

我使用一个子程序,使几个不同的哈希映射。 目前,我正在通过引用传递HashMap的,但这种冲突多次这样做,当它。 我应该通过价值路过的散列或通过哈希参考?

use strict;
use warnings;

sub fromFile($){
    local $/;
    local our %counts =();
     my $string = <$_[0]>;
    open FILE, $string or die $!;
    my $contents = <FILE>;
    close FILE or die $!;

    my $pa = qr{
        ( \pL {2} )
        (?{
            if(exists $counts{lc($^N)}){
                $counts{lc($^N)} = $counts{lc($^N)} + 1;
            }
            else{
                $counts{lc($^N)} = '1';
            }
        })
        (*FAIL)
    }x;

     $contents =~ $pa;

    return %counts;

}

sub main(){
    my %english_map = &fromFile("english.txt");
    #my %german_map = &fromFile("german.txt");
}

main();

当我单独运行不同的txt文件我没有得到任何的问题,但兼具我得到一些冲突。

Answer 1:

一对夫妇,你应该使用传递按引用的理由,但你的代码表明收益率的值的哈希值。

  • 你应该用my ,而不是local除了像内置变量$/ ,然后只有尽可能小的范围内尽可能。

  • 在子程序原型是几乎从来没有一个好主意。 他们做的非常具体的东西,如果你不知道那是什么,你不应该使用它们。

  • 调用子程序使用&印记,如&fromFile("english.txt")并没有因为Perl 4,大约二十年前是正确的。 它会影响至少两种不同的方式传递给子程序的参数,是一个坏主意。

  • 我不知道为什么你使用的文件名匹配与my $string = <$_[0]> 你作为参数传递的文件名通配符期待? 如果是这样,那么你将被打开和读取只有第一个匹配的文件,否则水珠是不必要的。

  • 词法文件句柄像$fh比裸字的文件处理好类似FILE ,并且将被隐式关闭它们被破坏时-通常是在要声明的块的结尾。

  • 我不知道你怎么哈希%counts被填充。 对自己没有正则表达式可以填写一个哈希,但我会信任你!

试试这个版本。 熟悉Perl的人会感谢你(讽刺!)不使用驼峰变量名。 它是难得一见main声明和调用子程序。 这是C,这是Perl的。

更新我已经改变了这个代码做你原来的正则表达式做了什么。

use strict;
use warnings;

sub from_file {

    my ($filename) = @_;

    my $contents = do {
        open my $fh, '<', $filename or die qq{Unable to open "$filename": $!};
        local $/;
        my $contents = <$fh>;
    };

    my %counts;
    $counts{lc $1}++ while $contents =~ /(?=(\pL{2}))/g;

    return \%counts;
}

sub main {
    my $english_map = from_file('english.txt');
    my $german_map  = from_file('german.txt');
}

main();


Answer 2:

三点意见:

不要混淆通过与引用传递引用

经过参考是使含有基准(的类型的值)一标量。

当它通过该参数不进行复制编译器经过参考的参数。

当它通过所述参数的副本编译器经过值的参数。

参数总是用在Perl引用传递

修改函数的参数(的元素@_ )将改变在呼叫者相应的变量。 这就是复制的参数公约存在的原因之一。

my ($x, $y) = @_;   # This copies the args.

当然,对于复制参数的主要原因是为了“名”出来的,但它的一些讨厌的惊喜,我们会使用的元素让我们节省了@_直接。

$ perl -E'sub f { my ($x) = @_; "b"=~/(.)/; say $x;    } "a"=~/(.)/; f($1)'
a

$ perl -E'sub f {               "b"=~/(.)/; say $_[0]; } "a"=~/(.)/; f($1)'
b

一个不能传递数组或散列作为在Perl的参数

可以传递到一个Perl子的唯一的事情是标量的列表。 (这也可以由一个返回的唯一的事。)

由于@a计算结果为$a[0], $a[1], ...在列表环境,

foo(@a)

是相同的

foo($a[0], $a[1], ...)

这就是为什么我们创造我们想要传递给子数组或散列的引用,并通过参考。

如果我们不这样做,数组或哈希将被评价,分为标量的列表,它会子里面被重建。 不仅是昂贵的,它在同类个案是不可能的

foo(@a, @b)

因为foo没有办法知道有多少参数被返回@a多少被退回@b

需要注意的是它可能使它看起来像一个数组或哈希正在为使用原型的参数传递,但原型只是导致自动创建到数组/哈希的引用,这就是实际传递到子。



Answer 3:

你可以使用一个参考或传递整个散列或阵列。 你的选择。 这里有两个问题可能让你选择一个比其他:

  1. 传递其他参数
  2. 内存管理

Perl并不真的有子程序参数。 相反,你只是传递的参数数组。 如果你子程序是看到什么该阵列具有更多的元素。 我不能这样做:

foo(@first, @second);

因为所有的我将传递是一个大阵,结合两者的所有成员。 这是哈希太真。 想象一下,一个程序,需要两个散列,并认为与普通按键的:

@common_keys = common(%hash1, %hash1);

同样,我将所有的按键和它们的值在两个散列成一个大阵。

解决这个问题的唯一方法是通过一个参考:

foo(\@first, \@second);
@common_keys = common(\%hash1, \%hash2);

在这种情况下,我路过那里这两个哈希存储在内存中的存储位置。 我的子程序可以使用这些哈希引用。 但是,你必须采取一些照顾,我会用第二种解释解释。

第二个原因通过参考是存储器管理。 如果我的数组或哈希是几十项,它其实并不重要,所有的东西。 然而,想象我在我的散列或数组10,000,000项。 复制所有成员可能需要相当多的时间。 按引用传递为我节省了内存, 但有一个可怕的代价 。 大多数时候,我使用的子程序为不影响我的主要程序的方式。 这就是为什么子程序想用自己的变量,你为什么要教有关变量的作用域大多数程序设计课程。

然而,当我通过一个参考,我打破了范围。 这里有一个简单的程序,没有通过的参考。

#! /usr/bin/env perl
use strict;
use warnings;

my @array = qw(this that the other);

foo (@array);

print join ( ":", @array ) . "\n";

sub foo {
    my @foo_array = @_;
    $foo_array[1] = "FOO";
}

注意子程序foo 1被改变阵列中传递的第二元件。 然而,即使我通过@array进入foo ,子程序不改变的值@array 。 这是因为子程序正在复印件(由创建my @foo_array = @_; 一旦子程序存在,副本中消失了。

当我执行这个程序,我得到:

this:that:the:other

现在,这里的同一个节目,但我传递一个引用,并在内存管理的兴趣,我用的参考:

#! /usr/bin/env perl
use strict;
use warnings;

my @array = qw(this that the other);

foo (\@array);

print join ( ":", @array ) . "\n";

sub foo {
    my $foo_array_ref = shift;
    $foo_array_ref->[1] = "FOO";
}

当我执行这个程序,我得到:

this:FOO:the:other

那是因为我没有在数组中传递,但该数组的引用。 这是持有相同的内存位置@array 。 因此,在我的子程序改变引用导致它在我的主要程序进行更改。 在大多数情况下,你不希望这样做。

您可以通过传递引用,然后复制,提及一个数组解决这个问题。 例如,如果我做到了这一点:

sub foo {
    my @foo_array = @{ shift() };

我会做我引用的副本到另一个阵列。 它可以保护我的变量,但它确实意味着我在抄袭我阵列转移到另一个物体需要时间和内存。 早在当我第一次在节目上世纪80年代,这是一个大问题。 然而,在这个时代技嘉内存和四核处理器,主要问题是没有内存管理,但可维护性。 即使你的数组​​或哈希含10万个条目,你可能不会注意到任何时间或内存的问题。

这也适用过周围的其他方式。 我可以从子程序返回引用到哈希或整个哈希。 很多人喜欢返回一个参考,但是这可能是有问题的。

在面向对象的Perl编程,我用引用来跟踪我的对象。 通常情况下,我不得不散列我可以用它来存储其他值,数组和哈希的参考。

在最近的一个项目,我正指望ID和他们多少次在日志文件中引用。 这被存储在对象(这仅仅是一个散列的引用)。 我将返回ID及其计数的整个散列的方法。 我可以这样做:

return $self->{COUNT_HASH};

但是,发生了什么事,如果用户开始修改引用我通过? 他们是真正不使用我的方法来添加,并从标识减去操纵我的对象。 这并不是说我希望他们做什么。 相反,我创建一个新的哈希值,然后返回到该散列的引用:

my %hash_counts = % { $self-{COUNT_HASH} };
return \%hash_count;

这种复制我引用到一个数组,然后我通过参照阵列。 这从外部操纵保护我的数据。 我仍然可以返回一个参考,但用户将无法再访问我的对象,而无需通过我的方法去。

顺便说一句,我喜欢用wantarray这给呼叫者他们希望他们的数据选择:

my %hash_counts = %{ $self->{COUNT_HASH} };
return want array ? %hash_counts : \%hash_counts;

这让我返回引用或取决于用户如何叫我对象的哈希:

my %hash_counts = $object->totals();      # Returns a hash
my $hash_counts_ref = $object->totals();  # Returns a reference to a hash

1脚注:该@_阵列所指向的相同的存储单元的调用子程序的参数。 因此,如果我通过在foo(@array)然后做$_[1] = "foo"; ,我会改变第二元件@array



文章来源: Pass by value vs pass by reference for a Perl hash