Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Method Calls on multiple objects

by Limbic~Region (Chancellor)
on Mar 26, 2004 at 23:48 UTC ( #340168=perlquestion: print w/replies, xml ) Need Help??

Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:

All,
I was looking on another Perl forum when I noticed an interesting question. Basically, is there a module that simplifies the same method call on a list of objects:
$obj_multi->new( $obj_1, $obj_2 ); $obj_multi->foo(); # $obj_1->foo(); $obj_2->foo();
I whipped this up real quick:
package MultiMethod; use strict; use warnings; use vars '$AUTOLOAD'; sub new { my $class = shift; my $self = \@_; return bless $self , $class; } sub DESTROY { return; } sub AUTOLOAD { my $self = shift; if ( $AUTOLOAD =~ /::([^:]+)$/ ) { $_->$1(@_) for @$self; } } 42;
Which I knew wasn't perfect. There is no error checking and there is also likely issues with ref counting and what not. Then it hit me, there is no need for a module:
MultiMethod( 'methodname', [], $file, $file2 ); sub MultiMethod { my $method = shift; my $args = shift; $_->$method(@$args) for @_; }
Finally, I promised I would ask around at the Monastery and update with any useful feedback. So how would you do it? Keeping in mind that you want it to be re-useable and as user friendly as possible.

Cheers - L~R

Replies are listed 'Best First'.
Re: Method Calls on multiple objects
by bart (Canon) on Mar 27, 2004 at 00:13 UTC

    No, it seems to me that what the original did, was allow all methods these object can do, on them all. In fact it sound a lot like tee — which is what I'd base the name of this module on.

    Anyway, you original approach looks good to me, except I'd cache the sub, so AUTOLOAD isn't repeatedly called for each method.

    sub AUTOLOAD { my $self = shift; if (my($method) = $AUTOLOAD =~ /::([^:]+)$/ ) { no strict 'refs'; *$AUTOLOAD = sub { my $self = shift; $_->$method(@_) for @$self; }; $self->$AUTOLOAD(@_); } }
    That appears to work, at first sight. I'm not sure it's actually better/faster.

    p.s. Your original calls don't look right, it should be

    my $obj_multi = MulitiObject->new( $obj_1, $obj_2 ); $obj_multi->foo($x, $y); # $obj_1->foo($x, $y); $obj_2->foo( +$x, $y);
    Oh, and make to constructor
    sub new { my $class = shift; return bless [ @_ ], $class; }
    I don't trust takling references to @_.
Re: Method Calls on multiple objects
by DamnDirtyApe (Curate) on Mar 27, 2004 at 01:58 UTC

    Nice module, L~R, but I'm just not seeing how this is inherently better than using what Perl already gives you.

    #! /usr/bin/perl use strict; package Dog; sub new { my $class = shift; my $self = {}; return bless $self, $class; } sub bark { print "Woof!\n"; } package main; my $fido = Dog->new(); my $woofy = Dog->new(); $_->bark for ( $fido, $woofy );

    Is this not just as effective? Or am I missing something?


    _______________
    DamnDirtyApe
    Those who know that they are profound strive for clarity. Those who
    would like to seem profound to the crowd strive for obscurity.
                --Friedrich Nietzsche
      DamnDirtyApe,
      Or am I missing something?
      Not really. You would have to modify it only slightly to allow argument passing. The big difference is that with:
      $obj_merged->bark( 'mailman' );
      You get the benefit of not having to remember how many/which objects (which could be of different classes) are in the merged object. It also becomes a heck of a lot easier depending on how many times you want to bark at the moon. This is why I do not think my second example would be desireable to the person asking either.

      Keep in mind I am not suggesting a new module for CPAN by any stretch of the imagination - just something to help someone who asked.

      Cheers - L~R
Re: Method Calls on multiple objects
by dragonchild (Archbishop) on Mar 27, 2004 at 01:59 UTC
    I'm a little confused ... What benefit does this provide? As far as I can tell, this is basically a rewriting (and complicating) of:
    $_->$method(@args) for @objects;

    Personally, I don't see a need for calling the same method on a number of objects. If you need an aggregate, create an aggregate. This, to me, would be a red flag in a code review.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      Or even capturing return values:

      my @rets = map { $_->$method( @args ) } @objs;

      Or allowing returns of lists:

      my @rets = map { [ $_->$method( @args ) ] } @objs;
        So, why would an aggregate be better? What syntactic sugar are you looking for?

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      Personally, I don't see a need for calling the same method on a number of objects. If you need an aggregate, create an aggregate. This, to me, would be a red flag in a code review.

      # make 8 random aliens @lifeforms = map { RandomSpaceTraveller->new(); } (0..8); # find out who is klingon @klingons = grep { $_->get_species() eq 'klingon' } @lifeforms; # blow the klingons away foreach (@klingons) { $kirk->shoot_at($_); };

      As evidenced above, I disagree. There are those of the functional school (of which I am quickly becoming a convert), who would say mixing OO and functional concepts in the same program is extremely funky in a good way.

      Another point -- What if you are implementing an aggregate? Well, you need to know how to dance in aggregate school.

        I'd agree with dragonchild. Yes, combining those styles is good. But strive to do it naturally, and you won't likely find the proposed method useful in many situations.

        Remember, many small utilities that fit together in flexible ways. That's the ticket. :-)

        So, in your example, the aggregate would be of the RandomSpaceTraveller objects, right? Personally, assuming there's nothing more to your example, I would write that as:
        $kirk->shoot_at( $_ ) for grep { $_->get_species eq 'klingon' } map { RandomSpaceTraveller->new } 0 .. 8;

        If you're implementing an aggregate ... we already have a perfectly good aggregate - it's called an array. If you need a named aggregate, use a hash. How does this additional indirection benefit us?

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      dragonchild,
      What benefit does this provide?
      You really have me there. The person asking for this didn't say why they needed/wanted it. The only benefit I can see to my first solution is that it saves you a lot of typing if you are using it all over the place. By sticking all the objects in an array, as you have shown, you get the same shorthand as if you stuck them all in a new object. I will be sure to pass this along.

      Cheers - L~R

Re: Method Calls on multiple objects
by Kanji (Parson) on Mar 27, 2004 at 02:57 UTC

    On the error checking front, you may want to throw can into the mix, so methodname is only called if the object implements it.

    sub MultiMethod { my $method = shift; my $args = shift; foreach my $object (@_) { $object->$method(@$args) if $object->can($method); } }

        --k.


      ->can isn't effective for checking methods handled by AUTOLOAD.

        It certainly is, if you program correctly. Either predeclare or override can().

        sub generate_method { my $self = shift; my ($name) = @_; my $method = <generate method and put into namespace>; return $method; } sub can { my $self = shift; my ($name) = @_; if ( <some rule that determines when a method is handled by AUTOLO +AD> ) { return $self->generate_method($name); } $self->SUPER::can($name); } sub AUTOLOAD { my $name = our $AUTOLOAD; $name =~ s/.*::([^:]+)$/$1/; my $self = shift; if ( <some rule that determines when a method is handled by AUTOLO +AD> ) { return $self->generate_method( $name )->(@_); } $self->SUPER::AUTOLOAD( @_ ); }

        Now it is ... kinda. It's slower and annoying, but can be done.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

Re: Method Calls on multiple objects
by adrianh (Chancellor) on Mar 27, 2004 at 16:52 UTC
    So how would you do it? Keeping in mind that you want it to be re-useable and as user friendly as possible.

    One of my favourite answers (right up there with "yes and no") - it depends ;-)

    If there is a natural aggregate object then that's the appropriate place for the behaviour to sit. If there isn't a natural aggregate object then I'd just use for directly.

    Having a special object or subroutine just to encapsulate a normal perl idiom would seem overkill, unless it would naturally form part of your object model.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://340168]
Approved by bart
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2021-11-30 19:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?