Hi All,
  I've hit a problem that has me a bit stumped and I'm not entirely sure which way to go towards a solution. The problem is I've got a sub routine that calls itself, and in doing so the initial my of my variables seems to loose it's effect.
  I'm aiming and scanning through and hash reference and deleting keys to hash and array references that are empty.
My test code:-
my $hashvar = { emp => {}, hemp => { splay => {}, hay => { go => 1, ho => {}, }, tay => [], may => [ 'way' ], }, }; sub HashClean { my ($hashref) = @_; if (ref ($hashref) eq "HASH") { foreach my $key (keys %$hashref) { print "W $key\n"; if (ref ($hashref->{$key}) eq "HASH") { print "HR $key\n"; if (keys (%{$hashvar->{$key}})) { print "HC $key\n"; &HashClean($hashref->{$key}); }#if else { print "HD $key\n"; delete $hashref->{$key}; }#else }#if if (ref $hashref->{$key} eq "ARRAY") { print "AR $key\n"; if ($#{$hashvar->{$key}}) { print "AC $key $#{$hashvar->{$key}}\n"; &HashClean($hashref->{$key}); }#if else { print "AD $key\n"; delete $hashref->{$key}; }#else }#if }#foreach }#if else { foreach my $key (@$hashref) { if (ref ($hashref->[$key]) eq "HASH") { if (keys (%{$hashvar->[$key]})) { &HashClean($hashref->[$key]); }#if else { # delete $hashref->[$key]; }#else }#if if (ref $hashref->[$key] eq "ARRAY") { if ($#{$hashvar->[$key]}) { &HashClean($hashref->[$key]); }#if else { # delete $hashref->[$key]; }#else }#if }#foreach }#else }#sub print Data::Dumper->Dump([$hashvar],[qw (::hashvar)]); &HashClean($hashvar); print Data::Dumper->Dump([$hashvar],[qw (::hashvar)]);

I know all the prints are a bit messy but I've put them in to try and figure out what it happening. Output is:-
$::hashvar = { 'hemp' => { 'may' => [ 'way' ], 'tay' => [], 'hay' => { 'ho' => {}, 'go' => 1 }, 'splay' => {} }, 'emp' => {} }; W hemp HR hemp HC hemp W may AR may AC may -1 W tay AR tay AC tay -1 W hay HR hay HD hay W splay HR splay HD splay W emp HR emp HD emp $::hashvar = { 'may' => [], 'hemp' => { 'may' => [ 'way' ], 'tay' => [] }, 'hay' => {}, 'tay' => [], 'splay' => {} };

So it appears that when it re-calls HashClean a new $hashvar isn't created and instead it merges the keys with the old one. Not what I was expecting... and I'm afraid I don't know how to get it to do what I was expecting :S

Help as always, much appreciated

Lyle

Update: DOH! Seems within the sub I'd accidently typed hashvar where I meant hashref. Changing it to hashref fixed things and it works as I originally expected. Thanks guys
Update 2 I found that when an array or hash contained only empty arrays or hashes you were left with empty hashes (as it wasn't empty before it's empty hash/array contents were removed). Not fully understanding GrandFathers code I ended up writing a mod of my original code so that it checks if anything was deleted and reruns each level if something was:-
Update 3 I found if you had empty structure within empty structures, within empty ones again, some of the initial ones were being left behind. This update returns whether it deleted anything to the previous level which re-runs if need.
sub HashClean { my ($hashref) = @_; my $deletes = 0; if (ref ($hashref) eq "HASH") { foreach my $key (keys %$hashref) { if (ref ($hashref->{$key}) eq "HASH") { if (keys (%{$hashref->{$key}})) { $deletes += &HashClean($hashref->{$key}); }#if else { delete $hashref->{$key}; $deletes++; }#else }#if if (ref $hashref->{$key} eq "ARRAY") { if ($#{$hashref->{$key}} >= 0) { $deletes += &HashClean($hashref->{$key}); }#if else { delete $hashref->{$key}; $deletes++; }#else }#if }#foreach }#if else { my $arraynum = 0; for (my $arraynum = 0; $arraynum <= $#$hashref; $arraynum++) { if (ref ($hashref->[$arraynum]) eq "HASH") { if (keys (%{$hashref->[$arraynum]})) { $deletes += &HashClean($hashref->[$arraynum]); }#if else { delete $hashref->[$arraynum]; $deletes++; }#else }#if if (ref $hashref->[$arraynum] eq "ARRAY") { if ($#{$hashref->[$arraynum]} >= 0) { $deletes += &HashClean($hashref->[$arraynum]); }#if else { delete $hashref->[$arraynum]; $deletes++; }#else }#if }#for }#else &HashClean($hashref) if $deletes; return $deletes; }#sub

In reply to Variable scoping when a sub calls itself?? by cosmicperl

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.