Johann Johann - 1 year ago 58
Perl Question

Pass by value vs pass by reference for a Perl hash

I'm using a subroutine to make a few different hash maps. I'm currently passing the hashmap by reference, but this conflicts when doing it multiple times. Should I be passing the hash by value or passing the hash reference?

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;
$counts{lc($^N)} = '1';

$contents =~ $pa;

return %counts;


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


When I run the different txt files individually I get no problems, but with both I get some conflicts.

Answer Source

For a couple of reasons you should use pass-by-reference, but the code you show returns the hash by value.

  • You should use my rather than local except for built-in variables like $/, and then for only as small a scope as possible.

  • Prototypes on subroutines are almost never a good idea. They do something very specific, and if you don't know what that is you shouldn't use them.

  • Calling subroutines using the ampersand sigil, as in &fromFile("english.txt"), hasn't been correct since Perl 4, about twenty years ago. It affects the parameters delivered to a subroutine in at least two different ways and is a bad idea.

  • I'm not sure why you are using a file glob with my $string = <$_[0]>. Are you expecting wildcards in the filename passed as the parameter? If so then you will be opening and reading only the first matching file, otherwise the glob is unnecessary.

  • Lexical file handles like $fh are better than bareword file handles like FILE, and will be closed implicitly when they are destroyed - usually at the end of the block where they are declared.

  • I am not sure how your hash %counts gets populated. No regex on its own can fill a hash, but I will have to trust you!

Try this version. People familiar with Perl will thank you (ironically!) for not using camel-case variable names. And it is rare to see a main subroutine declared and called. That is C, this is Perl.

Update I have changed this code to do what your original regex did.

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');