Search code examples
perlhashreferencesubroutine

How can I dereference a hash in a subroutine an work on it like in the calling routine?


I have a perl routine, that makes a hash out of a .csv-file. The values should be checked in a subroutine.

So I have a hash %my_values and call the subroutine check (\%my_values):

sub read_csv {
    ...

    # key:   headline entry
    # value: the value in the current row
    %my_hash;

    ...
    my ($has_error, $err_msg) = check (\%my_hash);
}

sub check {
    my($hash_ref) = @_;
    %my_hash = %$hash_ref;

    # Get the artikel number of the article
    $my_hash {'article_number'} = get_artnr($my_hash {'article'});
    if (not $my_hash{'article_number'}) {
        return 1, "Article $my_hash{'article'} not found!";
    }

    # check price (I'm in germany, there must be a conversation from ',' to '.')
    $my_hash {'price'} =~ s/,/./;
    if (not $my_hash{'price'} =~ m/^\d+(\.\d+)?$/) {
        return 1, "Invalid format of price";
    }

    return 0, "";
}

At first, this seems to work fine. But then i recognized, that neither the priceformat changed nor the key article_number is avalable.

Working directly on the reference made it:


# In this case, it works!
sub check {
    my($hash_ref) = @_;

    # Get the artikel number of the article
    $hash_ref->{'article_number'} = get_artnr($hash_ref->{'article'});
    if (not $hash_ref->{'article_number'}) {
        return 1, "Article $hash_ref->{'article'} not found!";
    }

    # check price (I'm in germany, there must be a conversation from ',' to '.')
    $hash_ref->{'price'} =~ s/,/./;
    if (not $hash_ref->{'price'} =~ m/^\d+(\.\d+)?$/) {
        return 1, "Invalid format of price";
    }

    return 0, "";
}

So i think %my_hash = %$hash_ref; makes a copy of the reference instead of dereferencing.

How can I dereference a hash in a subroutine an work on it like in the calling routine?


Solution

  • Here is an example using the new feature called refaliasing introduced in Perl 5.22 (optionally combined with the declared_refs feature introduced in 5.26)

    use v5.26;
    use warnings;  # IMPORTANT: this line must come before "use experimental"
    use strict;
    use feature qw(say); 
    use experimental qw(declared_refs refaliasing);
    
    {  # <-- introduce scope to avoid leaking lexical variables into subs below
        my %hash = (a=>1, b=>2);
        check(\%hash);
        say "Value of 'a' key is now: ", $hash{a};
    }
    
    sub check {
        my (\%hash) = @_;
    
        $hash{a} = 3;
    }
    

    Output:

    Value of 'a' key is now: 3
    

    Alternatively you can use the arrow operator:

    sub check {
        my ($hash) = @_;
    
        $hash->{a} = 3;
    }