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

I have code like this that is repeated over and over again in the same script.
my @list_var; if (ref $giant_hash{key} eq 'ARRAY') { for (@{ $giant_hash{key} }) { push @list_var, My::Class::Child->new( $_ ); } } else { push @list_var, My::Class::Child->new( $giant_hash{key} ); }
I'm trying to refactor it into one subroutine. The only thing that changes is the @list_var and the My::Class::Child.

I did this:

our %giant_hash = Module->whatever(); sub _fill_lists { my ($thing, $thing_ref) = @_; my ($method , $flag); SWITCH: for ($thing) { /XXX/ && do { $method = \&My::Class::XXX->new; last; }; /YYY/ && do { $method = \&My::Class::YYY->new; last; }; /etc/ && do {$and_so_on; last; }; } if (ref $giant_hash{$thing} eq 'ARRAY') { for (@{ $giant_hash{$thing} }) { push @{ $thing_ref }, $method->( $_ ); } } else { push @{ $thing_ref }, $method->( $giant_hash{$thing} ); } }
But I'm getting a reference violation error that I can't quite understand why.
Can't use string ("XXX") as an ARRAY ref while "strict refs" in use at + ./testme.pl line 76.
Nor am I feeling inspired right about now with a decent solution.

Any ideas?

Replies are listed 'Best First'.
Re: Need Help Refactoring Method Calls
by rhesa (Vicar) on Jan 30, 2006 at 00:59 UTC
    I don't think you can take references to class methods like that.
    The obvious solution (to me at least) is to make the package name the variable:

    sub _fill_lists { my ($thing, $thing_ref) = @_; my $class = "My::Class::$thing"; # + <== if (ref $giant_hash{$thing} eq 'ARRAY') { for (@{ $giant_hash{$thing} }) { push @{ $thing_ref }, $class->new( $_ ); # + <== } } else { push @{ $thing_ref }, $class->new( $giant_hash{$thing} ); # + <== } }
      Wow. I really did think of something like this at first, but I concluded that it would be a soft reference that the 'warnings' pragma wouldn't catch but Perl Lint would. I didn't want to use a fancy soft reference like that.

      However, since no one else has commented that what you wrote was a soft reference (even though it still looks like one to me), I decided to run an example thru Perl Lint, and sure enough, it passes.

      # cat test_me.pl #!/usr/bin/perl -w use strict; use warnings; use Top::Middle; my $class = "Top::Middle"; my $obj = $class->new( 'info' => 'some_data_or_whatever' ); $obj->method(); exit 0;
      # perl -MO=Lint test_me.pl test_me.pl syntax OK
        I don't think we're dealing with soft references here. The $class is just a string. Perl lets you write barewords for package names as well, but really it's just the package name that you're passing around.

        Remember, Foo->new, or $class->new, both translate to new( Foo ) or new( $class ), which is simply passing the string into the new() call.

        (I know, I should probably have written Foo::new('Foo'), plus there's some method dispatching logic buried in the ->, but that doesn't detract from the fact.)

Re: Need Help Refactoring Method Calls
by dragonchild (Archbishop) on Jan 30, 2006 at 03:15 UTC
    rhesa's solution is the best one. However, if you absolutely have to take a reference to a method, you will want to do it this way:
    my $meth = My::Class::XXX->can( 'new' ); my $method = sub { $meth->( 'My::Class::XXX', @_ ); };

    If you don't understand that syntax, don't use it. If you do, that's how you take a reference to a method.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?