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

I'm trying to use /usr/lib/kbd/ keymaps to create svgalib keymap. I'm trying to process keymap_file1 and all included keymaps and collect data into single hash, then fully load keymap_file2 and produce mapping table from keymap_file1 to keymap_file2. How can I use the same hash to collect all the values? Now only the values from the last file processed are on hash.
#!/usr/bin/perl use warnings; use strict; sub load_keymap { my $file_name = shift; my %keymap = %{$_[0]}; my $line; local *IN; $file_name ="gzip -dc $file_name |" if $file_name =~ /\.gz$/; print "Loading $file_name...\n"; open (IN, $file_name) or die "Could not open $file_name: $!"; while (defined($line = <IN>)) { if ($line =~ /include "(.+)"/) { load_keymap("$1.inc.gz", \%keymap); } next if $line !~ /keycode\s+(\d+)\s*=\s*(\S+)/; $keymap{$2} = $1; #print "$2 = $1\n"; } close(IN); return \%keymap; } my (%map_from, %map_to) = (); my $key = 0; $key = load_keymap("de.kmap.gz", \%map_from); %map_from = %$key; foreach $key (keys %map_from) { print "$key = $map_from{$key}\n"; }

Replies are listed 'Best First'.
Re: Recursion + hash
by jbert (Priest) on Dec 11, 2001 at 16:32 UTC
    The simple answer is that it is due to the line:
    my %keymap = %{$_[0]};
    What does this do? Well, 'my' creates a new lexical variable, in this case '%keymap'. What happens next is that this new variable is initialised with the hash you get from dereferencing $_[0] (hopefully a hash reference). i.e. You are copying the data to a new hash.

    So far so good, except that this new hash is thrown away (as all lexical variables are) at the end of the scope. In this case, your 'my' declaration has scope of the load_keymap function.

    The easiest way to get around this is to try not to dislike using references directly. i.e. If your load_keymap function began with:

    my $file_name = shift; my $hr = shift;
    and continued with:
    ... $hr->{$2} = $1;
    Then all would be well.

    Below is the code snippet I used to try and be sure what was going on. You can change between the types of behaviour by altering the $ALWAYS_USE_REF constant at the top.

    Hope this helps.

    #!/usr/local/bin/perl use strict; my $ALWAYS_USE_REF = 1; main( @ARGV ); exit 0; sub main { my %hash; recursively_foo_my_hash( \%hash, 5 ); print_hash_ref_contents( \%hash ); return 1; } sub recursively_foo_my_hash { my $hr = shift; print "RFMH called with hash ref: $hr\n"; my %new_hash = %{ $hr }; my $depth = shift; $hr = \%new_hash unless $ALWAYS_USE_REF; return 1 unless $depth > 0; $hr->{"foo_$depth"} = rand(); # Add some data recursively_foo_my_hash( $hr, $depth - 1 ); return 1; } sub print_hash_ref_contents { my $hr = shift; print "Hash ref [$hr] contains:\n"; foreach my $k ( sort keys %$hr ) { print "$k -> $hr->{$k}\n"; } return 1; }
Re: Recursion + hash
by rob_au (Abbot) on Dec 11, 2001 at 16:32 UTC
    I recently had a similar issue with hash references and sub-routine recursion and explored two solutions :-

    1. The first solution was to use a single blessed hash which was referenced through each iteration of the subroutine. An example of this solution can be seen at Local::SiteRobot - a simple web crawling module - In particular, have a look at the _crawl subroutine where this technique has been employed.
    2. The second solution involved the shift from a recursion based iteration through elements to stack-based iteration. This technique meant that the recursion process, deemed most evil depending on whom you talk to, could be replaced in its entirity. An example of such code can be seen in the File::Find module shipped with Perl 5.6 and later (which can be compared to the recursion based File::Find module shipped with earlier distributions).

      An example of stack-based code may look like this:

      foreach my $element (@list) { # ... process element, search for sub-elements ... splice ( @list, ($depth_first_processing) ? 0 : @list, 0, $subelement ); }

      A quick note on this code - The variable $depth_first_processing allows new sub-elements can be either added to the end of the list (breadth-first iteration) or as elements to be iterated through within the next loop (depth-first iteration).

      This structure of stack-based iteration has been incorporated into the current version of WWW::SimpleRobot.

     

    perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'