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

Hey all, i'm writing a pretty specific reporting tool for some data crunching i'm working on, and stumbled across a little issue that i'd missed before. Anyway, lets say I have a class that's implemented with a hashref, that includes the data member "Big_List", i.e.
$self->{'Big_List'} = [many elements];
Then, in another method, i need to operate on each element of the big list. I could write a method called GetBigList, and methods that want to operate on the list could access each element by looping through the return results of GetBigList()
sub GetBigList { my $self = shift; return $self->{'BigList'}; } sub SomeOperation { my $self = shift; foreach my $element ( @{$self->GetBigList()} ) { # do stuff to element, etc. } }
... and encapsulation is happy.

But, there is a lot of overhead because GetBigList() returns a copy of the list to SomeOperation(). So, i could rewrite SomeOperation() to look like:
sub SomeOperation { my $self = shift; foreach my $element ( @{ $self->{'Big_List'} } ) { # do stuff to element, etc. } }
And i have broken encapsulation, but my scripts should incur much less overhead.

I'm really on the fence on this one, i'm generally VERY strict about not breaking encapsulation, but this situation may call for it. Of course, breaking encapsulation usually comes back and bites one on the posterier as updates occur.

Any thoughts?

Replies are listed 'Best First'.
Re: Array copy or encapsulation break?
by Aristotle (Chancellor) on Aug 06, 2003 at 16:56 UTC
    # ... return $self->{'BigList'}; # ... foreach my $element ( @{ $self->{'Big_List'} } ) { # ...
    Where do you see a list of the elements getting returned? You're getting a list with a single scalar - an array reference. You're even dereferencing it yourself in the for loop.

    Makeshifts last the longest.

      D'oh, you're correct. I don't know what i was thinking. I think i was going to dereference and return in GetBigList(), so that callers don't have direct access to the object's internals. But if i keep GetBigList() private, it should be ok.
        Now, you're entering a bigger question - what are you doing with the return from GetBigList()? It is usually considered better practice to provide that functionality within the class and not expose the internals through getters/setters. Often, getters/setters are used because pure OO is almost never feasible, for non-programming reasons.

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

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Array copy or encapsulation break?
by adrianh (Chancellor) on Aug 06, 2003 at 17:02 UTC

    Two alternatives spring to mind.

    Rather than returning the list with GetBigList() return an iterator. So you would apply an operation something like:

    my $i = $foo->get_big_list; while (my $element = $i->next) { # do stuff to $element };

    Alternatively, have a routine in your class that takes an anonymous subroutine and applies it to each element.

    # in your class sub apply_to_elements { my ($self, $coderef) = @_; $coderef->($_) foreach @{ $self->{'Big_List'} }; }; # somewhere else $foo->apply_to_elements( sub { my $element = shift; # do stuff to $element } );
Re: Array copy or encapsulation break?
by broquaint (Abbot) on Aug 06, 2003 at 17:01 UTC
    As it stands you're returning a single scalar, so there isn't a great deal of overhead there. I imagine there's more overhead in the method call than the returning of the scalar. If however your method did this
    return @{ $_[0]->{BigList} };
    That would be copying the list, so I wouldn't be worrying about your method for the time being :)
    HTH

    _________
    broquaint

Re: Array copy or encapsulation break?
by shemp (Deacon) on Aug 06, 2003 at 17:09 UTC
    Update:I forgot to mention part of the deal with the big list, its actually a hashref and i want to return the keys. So its something like:
    sub GetBigList { my $self = shift; return keys( %{$self->{'BigList'}} ); }
    keys() references the elements, but when the list is returned, a copy is made.

    But as was mentioned above, i could use an iterator, in this case, each() could work. I may want to have a reset method though.

      If the list is a hash, you could tie that hash to your your package and give the caller the tied handle. That way, they can manipulate the hash with all the familiar hash operators (keys, values, each etc. and the standard method of resetting these iterators), but you get to intervene in every operation and vet what they do.

      Using ties this way makes for a nice perlish and familar standard interface to class attributes, including using them as lvalues, without relinguishing control. Ties aren't the fastest things in the world, but they're no slower than calling setters and getters.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
      If I understand your problem, I can solve it! Of course, the same can be said for you.