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

Dear all,
I am trying to learn Perl and I am stuck with the hash section. I would like to implement a function (subroutine) that I could call when required to populate the hash with new elements.
The way I have done it is to create a FOR loop in which at each cycle a function is called; the function gets the key and value and the link to the hash. The function then returns the hash.
In theory the function gets the variables directly from the keyboard but for this example I have set a variable with some strings.
The function I have implemented actually properly gets the variables and store them in the hash, however at each cycle the hash is reset so the hash is not populated but just hosts the last couple of values that is fed with.

Is there a way to store the values permanently, so that the hash can be populated a bit at the time?

Thank you for any help.
Gigiux

here is the code i have written

use strict; my(@myArray); my(%myHash); my($myLoop, $key, $val, $myTemp, $i); @myArray = qw( sean connery george lazemby roger moore timothy dalton +pierce brosnan ); $myLoop = 5; print("The array is: @myArray\n"); for($i = 0; $i < $myLoop; $i++){ print("\nElement number $i\n"); $key = shift(@myArray); $val = shift(@myArray); print("The key is: $key\nThe value is: $val\n"); %myHash = &makeHash($key, $val, \%myHash); print("The hash at cycle $i is: "); print %myHash; $myTemp = $myHash{$key}; print("\nThe actual value for \"$key\" is: $myTemp\n"); } print("\nThe hash is not functional, in fact the value for \"sean\" is +: "); $key = "sean"; $myTemp = $myHash{$key}; print $myTemp; print("\n(that was an empty result)\n"); sub makeHash { my($key); my($val); my(%subHash); $key = shift @_; $val = shift @_; %subHash = shift @_; print("In the function, the key is $key and the value is $val\n"); print("The hash at the beginning is: "); print %subHash; $subHash{$key} = $val; print("\nThe hash at the end is: "); print %subHash; print("\n"); return(%subHash); }

the result from the first two cycles and at the end of the loop is as follows:

The array is: sean connery george lazemby roger moore timothy dalton p +ierce brosnan Element number 0 The key is: sean The value is: connery In the function, the key is sean and the value is connery The hash at the beginning is: HASH(0x1bfd938) The hash at the end is: HASH(0x1bfd938)seanconnery The hash at cycle 0 is: HASH(0x1bfd938)seanconnery The actual value for "sean" is: connery Element number 1 The key is: george The value is: lazemby In the function, the key is george and the value is lazemby The hash at the beginning is: HASH(0x1bfd938) The hash at the end is: georgelazembyHASH(0x1bfd938) The hash at cycle 1 is: HASH(0x1bfd938)georgelazemby The actual value for "george" is: lazemby ... The hash is not functional, in fact the value for "sean" is: (that was an empty result)

Note: even if I remove the link to the hash, the results are slightly different ("sean" is kind of stuck to the array but it is lost at the end of the implementation) but the hash does not get properly populated.

Replies are listed 'Best First'.
Re: How to populate a HASH using a FOR loop to a FUNCTION
by LanX (Saint) on Dec 24, 2014 at 00:24 UTC
    Tl;dr but

    %myHash = &makeHash($key, $val, \%myHash)

    Skip the left part and you are fine, no need to reset the hash. :)

     makeHash($key, $val, \%myHash)

    update

    Your makeHash() is seriously broken.

    Please have a look at perlref to understand how references work.

    update
    Better (among many things)

    my $subHashRef = shift @_; $subHashRef->{$key} = $val;

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

Re: How to populate a HASH using a FOR loop to a FUNCTION
by BillKSmith (Monsignor) on Dec 24, 2014 at 04:52 UTC
    Your only serious error is that you do not copy the hash into your subroutine when you get the other arguments. Note that you do have to dereference the reference.
    my($key); my($val); my(%subHash); $key = shift @_; $val = shift @_; %subHash = %{shift @_};
    With this addition, your subroutine will make a copy of the existing hash, add a key/value pair to the copy, and return the copy. Your main program overwrites the old hash with the new one. I believe that this is what you intend.
    Bill
      Dear Bill, thank you your solution works just fine! I am learning Perl 5.8 but since I am doing it on my own I lack most of the best tricks for efficient scripts. My best wishes of happy new year, G
        G, You are doing a good job learning the basic concepts. Your style needs a great deal of improvement. You should find all of the other replies helpful in this respect. Happy new year to you too.
        Bill
Re: How to populate a HASH using a FOR loop to a FUNCTION
by Laurent_R (Canon) on Dec 25, 2014 at 00:19 UTC
    You are working too much. ;-) Far too much in fact.

    This is a first rewrite of your code, keeping your code architecture but correcting a couple of mistakes and simplifying the code. We are down to 22 code lines:

    use strict; use warnings; use Data::Dumper; my %myHash; my @array = qw( sean connery george lazemby roger moore timothy dalton + pierce brosnan ); print("The array is: @array\n"); for my $i (0..4) { print("\nElement number $i\n"); my ($key, $val) = splice @array, 0, 2;; print("The key is: $key\nThe value is: $val\n"); populateHash($key, $val, \%myHash); print"The hash at cycle $i is: ", Dumper \%myHash; } print Dumper \%myHash; sub populateHash { my ($key, $val, $hashref) = @_; print "In the function, the key is $key and the value is $val\n"; $hashref->{$key} = $val; }
    And this is the output: Now, this is my second rewrite of your code, now just 12 lines:
    use strict; use warnings; use Data::Dumper; my %myHash; my @array = qw( sean connery george lazemby roger moore timothy dalton + pierce brosnan ); for my $i (0..4) { my ($key, $val) = splice @array, 0, 2;; $myHash{$key} = $val; } print Dumper \%myHash;
    which will print this:
    $ perl populate_hash.pl $VAR1 = { 'timothy' => 'dalton', 'sean' => 'connery', 'roger' => 'moore', 'pierce' => 'brosnan', 'george' => 'lazemby' };
    The main difference here is that, considering that populating the hash really takes just 1 line of code, calling a function for just doing this is overkill.
      ... populating the hash really takes just 1 line of code ...

      E.g., for the given example data of the OP:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my @array = qw( sean connery george lazenby roger moore timothy dalto +n pierce brosnan ); my %myHash = @array; dd \%myHash; " { george => "lazenby", pierce => "brosnan", roger => "moore", sean => "connery", timothy => "dalton", }

        Sure, you're right. I wanted to start keeping the OP's general architecture while fixing its defects, and then to simplify it in two steps. I originally thought of completing my post with a direct assignment as you did, but I forgot to do it when completing the post. Thanks for doing it. This is of course the easiest solution.
      I prefer the OP's approach. Functions should return their results through the return rather than through a reference. I believe that most of his additional code is code that he added to debug the problem himself before he posted. If so, he should be commended. Your use of Dumper and your attention to scope are details he should learn.
      Bill
        > Functions should return their results through the return rather than through a reference.

        Plz be aware that passing and returning hashes as lists can cause much useless overhead, if the original hash has to be overwritten.

        Cheers Rolf

        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        Functions should return their results through the return rather than through a reference.
        Hi Bill,

        Yes, I agree in general and often try to write "pure" functions, but I am at the same time generally quite reluctant at the idea of passing around many times a data structure, because this is quite inefficient, as already pointed out by LanX. (Granted, it does not really matter with such small array and hash, but I am usually manipulating fairly large data structures.)

        Anyway, I would not do it the way I described in my first code sample, which aimed at keeping the general code structure of the OP, which was passing a reference to the hash. BTW, this is not very useful here in view of the fact that the hash is in fact a global variable.

        If I wanted to populate the hash in a separate function (which is not really needed in such a simple case, but I am often doing this type of thing when populating the hash is more complicated and requires, for example, to read from one or several file(s) and process the content before actually storing keys and values into the hash), I would call that function only once and pass to it a reference to the array and then return the hash or, when the data is large, return a reference to the hash. Something along these lines:

        use strict; use warnings; use Data::Dumper; my @array = qw( sean connery george lazemby roger moore timothy dalton + pierce brosnan ); my $hash_ref = populate_hash(\@array); print Dumper $hash_ref; sub populate_hash { my $array_ref = shift; my %hash; for (0..4) { my ($key, $val) = splice @$array_ref, 0, 2; $hash{$key} = $val; } return \%hash; }
        But, again, this is only for the sake of argument: for such a simple case, I would not even use a separate function, and the easiest solution is the one provided by AnomalousMonk.
        Dear Bill,
        you are right: all the text I have used herein was just for debugging. The actual function I would like to create would just populate a given hash when called in different points within the main scripts. And at the moment I have no idea what Dumper is...
        G
      Dear Lauren,
      thank you for your reply, I will need some times to digest it.
      Meanwhile, my best wishes of happy new year,
      G