Domanda

I have a hash of arrays and a normal array, depending on the circumstances (ie what options the user chooses when the program is running) only one of these will be defined.

Example code to demonstrate problem:

my %hashofarrays;
my @array;

#...
#Some code between here where either %hashofarrays or @array gets defined
#...

if (defined @array) {

    foreach my $var1 (@array) {
        print "var1 is: $var1\n";
        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1);
    }

} 

if (defined %hashofarrays) {

    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {
            call_subroutine($var1);
            print "Something else is printed";
            call_anothersubroutine($var1);
            call_differentsubroutine($var1);
        }
    }
}
   

As you can see in the code above depending on whether the @array is defined or whether the %hashofarrays is defined it will run the corresponding if statement.

The problem:

The issue with this though is that the following lines of code are duplicated in both if statements:

    call_subroutine($var1);
    print "Something else is printed";
    call_anothersubroutine($var1);
    call_differentsubroutine($var1);

Obviously, if these foreach loops contained a lot of code, that would mean a huge amount of code would be duplicated.

Is there any way/what is the best way this duplication of code can be avoided when it comes to the foreach loop?

In effect, is there a way to do something like the following: (I am well aware this code will not work, but explains what I am trying to achieve)

if (defined @array) {
    foreach my $var1 (@array) {
} elsif (defined %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {

} #ending bracket of if statement

        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1); 

}  #ending bracket of whatever foreach loop is used

I may well be overlooking something obvious here but cannot see a logical way to do this?

È stato utile?

Soluzione

First of all, defined @array and defined %hashofarrays is wrong. They are always defined. You want if (@array) and if (keys %hashofarrays) to test if they contain elements. You should have even gotten an warning defined(@array) is deprecated!

What you want is another subroutine.

sub loop_body {  # just use some better name!
  my ($var) = @_;
  call_subroutine($var);
  print "Something else is printed";
  call_anothersubroutine($var);
  call_differentsubroutine($var);
}

Then:

if (@array) {
    foreach my $var1 (@array) {
        print "var1 is: $var1\n";
        loop_body($var1);
    }
} elsif (keys %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {
            loop_body($var1)
        }
    }
}

You can also use callbacks to make your solution more flexible:

 sub loop_with_cb {
   my $cb = shift;
   for my $var (@_) {
     if ($cb) {
       local $_ = $var; # make $_ visible to the callback
       $cb->($var);
     }
     call_subroutine($var);
     print "Something else is printed";
     call_anothersubroutine($var);
     call_differentsubroutine($var);
   }
 }

Then:

if (@array) {
    loop_with_cb(
      sub { print "var1 is: $_\n" },
      @array,
    );
} elsif (keys %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        loop_with_cb(undef, @{ $hashofarrays{$key} });
    }
}

Altri suggerimenti

Using another function might be helpful,

if (@array) {
    my_func(\@array);
} 
if (%hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        my_func($hashofarrays{$key});
    }
}

sub my_func {
    my ($arr) = @_;

    foreach my $var1 (@$arr) {
        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1);
    }

}

As amon said, if (defined @array) is wrong, but I wouldn't even bother with if (@array) unless you're going to have an else. You can go straight to for (@array) and, if @array is empty, it will iterate zero times, essentially skipping over the loop just like the if would.

With that out of the way, it looks like you want to do the same thing for every element in @array and every key in %hash of arrays, right? You don't even need a sub for that:

for (@array, map { @$_ } values %hashofarrays) {
  my $var1 = $_;
  print "var is: $var1\n";
  call_subroutine($var1);
  print "Something else is printed";
  call_anothersubroutine($var1);
  call_differentsubroutine($var1);
}

Edit: Revised code to loop over the contents of the arrays in %hashofarrays rather than its keys. This still isn't an exact match for the code in the initial question, since it prints $var1 when looping over the hoa instead of printing the hash keys, but it looks like there's a good chance that the OP's real use case doesn't use the key for anything other than indexing into the hash, in which case it's not really needed anyhow (since we can use values %hoa instead).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top