通过值传递vs传递Perl哈希的引用

Joh*_*ann 5 perl hash pass-by-reference pass-by-value

我正在使用子程序来制作一些不同的哈希映射.我目前正在通过引用传递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();
Run Code Online (Sandbox Code Playgroud)

当我单独运行不同的txt文件时,我没有遇到任何问题,但两者都有一些冲突.

ike*_*ami 11

三条评论:

不要混淆通过参考与通过引用

传递引用是传递包含引用(一种值)的标量.

编译器通过一个参数作为参考,当它通过该参数不进行复制.

编译器在传递参数副本时按值传递参数.

参数总是通过Perl中的引用传递

修改函数的参数(元素@_)将更改调用者中的相应变量.这是复制参数的约定存在的原因之一.

my ($x, $y) = @_;   # This copies the args.
Run Code Online (Sandbox Code Playgroud)

当然,复制参数的主要原因是"命名"它们,但它使我们免于通过@_直接使用元素获得的一些令人讨厌的惊喜.

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

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

在Perl中,无法将数组或散列作为参数传递

唯一可以传递给Perl子的是标量列表.(它也是唯一一个可以归还的东西.)

@a评估到$a[0], $a[1], ...列表上下文,

foo(@a)
Run Code Online (Sandbox Code Playgroud)

是相同的

foo($a[0], $a[1], ...)
Run Code Online (Sandbox Code Playgroud)

这就是为什么我们创建对我们想要传递给sub并传递引用的数组或哈希的引用.

如果我们不这样做,那么数组或散列将被计算为标量列表,并且必须在子内部重建.这不仅价格昂贵,而且在某些情况下也是不可能的

foo(@a, @b)
Run Code Online (Sandbox Code Playgroud)

因为foo无法知道返回了多少个参数@a以及返回了多少个参数@b.

请注意,可以使用原型将其看作数组或散列作为参数传递,但原型只会导致自动创建对数组/散列的引用,这就是实际传递给sub的内容.


Bor*_*din 9

出于几个原因,您应该使用pass-by-reference,但是您显示的代码会按值返回哈希值.

  • 你应该使用my而不是local像内置变量那样使用$/,然后只使用尽可能小的范围.

  • 子程序的原型几乎不是一个好主意.他们做了非常具体的事情,如果你不知道那是什么,你就不应该使用它们.

  • &fromFile("english.txt")自从大约二十年前的Perl 4以来,使用&符号来调用子程序就像是一样.它以至少两种不同的方式影响传递给子程序的参数,这是一个坏主意.

  • 我不确定你为什么要使用文件glob my $string = <$_[0]>.您是否期望在作为参数传递的文件名中使用通配符?如果是这样,那么你将打开并只读取第一个匹配的文件,否则不需要glob.

  • 类似于$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();
Run Code Online (Sandbox Code Playgroud)

  • 呃,"腐败"?我猜你的意思是它绕过原型,如果你使用`&fn;`没有parens,它可以向被调用的函数显示当前的`@ _`.但是"腐败"并不是我想用自己的一个词.我很惊讶你没有建议拼写出`glob`函数,以免角度算子被休闲维护者混淆为`readline`函数.谢谢你提到骆驼案的丑陋. (2认同)