CommaSplice has asked for the wisdom of the Perl Monks concerning the following question:

I have a hash in my main code that I wish to bring into a subroutine, modify and then return to print the data values.

Problem: I can't get the modified hash to return to the main code properly. When I try to print a hash value in my code I get the value from the original hash, and not the modified one. Or, I get a warning that the hash reference is undefined.

Here's an example I made of my problem using Star Trek references - I wish to replace the values in the hash (%starship) with different numbers if I find a match for the captain's name in the variable ($captains) input into the subroutine (trivia).

use strict; use warnings; my $captains = "kirkpicard"; my %starship = ( 'EntTOS'=>0, 'EntA'=>0, 'EntD'=>0, 'EntE'=>0, ); trivia ($captains,\%starship); print "Kirk was on Enterprise #: $starship{EntTOS}, $starship{EntA}\ +n"; print "Picard was on Enterprise #: $starship{EntD}, $starship{EntE}\n" +; sub trivia {, my ($captains, $starship_ref) = shift; %starship = %{$starship_ref}; if ($captains =~ m/(kirk)/) { $starship {'EntTOS'} = 1; $starship {'EntA'} = 2; } if ($captains =~ m/(picard)/) { $starship {'EntD'} = 4; $starship {'EntE'} = 5; } return %starship; }

The output I should get is as follows:

Kirk was on Enterprise #: 1, 2 Picard was on Enterprise #:4, 5

Any ideas how to make it so?

Replies are listed 'Best First'.
Re: Replacing original hash with modified hash returned from subroutine
by hippo (Archbishop) on Oct 27, 2017 at 15:05 UTC

    You are already passing a reference. Don't copy it and don't return anything (as your sub is called in void context anyway) and assign the args from @_ instead of shift:

    use strict; use warnings; my $captains = "kirkpicard"; my %starship = ( 'EntTOS'=>0, 'EntA'=>0, 'EntD'=>0, 'EntE'=>0, ); trivia ($captains,\%starship); print "Kirk was on Enterprise #: $starship{EntTOS}, $starship{EntA}\ +n"; print "Picard was on Enterprise #: $starship{EntD}, $starship{EntE}\n" +; sub trivia { my ($captains, $starship_ref) = @_; if ($captains =~ m/(kirk)/) { $starship_ref->{'EntTOS'} = 1; $starship_ref->{'EntA'} = 2; } if ($captains =~ m/(picard)/) { $starship_ref->{'EntD'} = 4; $starship_ref->{'EntE'} = 5; } }
Re: Replacing original hash with modified hash returned from subroutine
by 1nickt (Canon) on Oct 27, 2017 at 17:47 UTC

    Hi, This cannot be your code; it does not compile. After removing the comma from the opening brace of the subroutine, I get "Can't use an undefined value as a HASH reference".

    Maybe you want something more like this?

    use strict; use warnings; use feature qw/ say /; my @captains = qw/ kirk picard popeye /; my $commanded_by = trivia( @captains ); for my $captain ( sort keys %{ $commanded_by } ) { say sprintf '%s was on Enterprise #: %s', ucfirst $captain, join ', ', @{ $commanded_by->{ $captain } }; } sub trivia { my @captains = @_; my %commands = ( kirk => [ 'EntTOS', 'EntA' ], picard => [ 'EntD', 'EntE' ], ); my %output; $output{ $_ } = $commands{ $_ } || [] for @captains; return \%output; } __END__
    Output:
    $ perl 1202155.pl Kirk was on Enterprise #: EntTOS, EntA Picard was on Enterprise #: EntD, EntE Popeye was on Enterprise #:

    Hope this helps!


    The way forward always starts with a minimal test.
Re: Replacing original hash with modified hash returned from subroutine
by shadowsong (Pilgrim) on Oct 28, 2017 at 21:12 UTC

    Hi CommaSplice,

    Your issue is the first line of your trivia subroutine.

    $starship_ref isn't being assigned anything – the call to shift returns a scalar that you're attempting to assign to a list...

    Change:

    my ($captains, $starship_ref) = shift;

    to:

    my $captains = shift; my $starship_ref = shift;

    Hope that helps.

    Best Regards,
    Shadowsong