http://www.perlmonks.org?node_id=458865

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

Esteemed Monks,

assuming I have a classic object, ie a blessed anonymous hash, which contains a chunk of data, and I want to clean out the data without actually deleting the object itself, what would be the best way to go about it? So far all I've been able to come up with would to be a

my $self = shift; foreach my $key (keys(%{$self})) { delete $self->{$key}; }
kind of solution but there has to be a better approach to this. Is there one? I searched the docs extensively but was unable to come up with a better idea.

edit: small example to clarify what I mean

edit 2: put the example between readmore tags.

package Celebrity; use strict; use warnings; my $security = Guard->new; while(1) { sleep(3600); $security->check; } package Guard; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless($self, $class); $self->{'Hours_on_the_job'} = 0; return $self; } sub check { #crawl through the bushes, meet people, shoot them #collect all sorts of data $self->{'Data'} = "blahdiblah"; $self->{'Moredata'} = "dumdidum"; $self->{'Yetmoredata'} = "whatever dude"; $self->{'Hours_on_the_job'} += 1; if($self->{'Hours_on_the_job'} == 6) { #long live the union! #file an extensive report, go home #new guard takes over * cleaning out the object would happen here * $self->{'Hours_on_the_job') = 0; } }
Now our Celebrity doesn't care who the actual guard is, he/she just wants to sleep() safely. So the object itself needs to take care of cleaning out the data without having to bother the calling script. Hope that clears it up a bit :-)

Remember rule one...

Edit by castaway - added missing closing small tag

Replies are listed 'Best First'.
Re: "cleaning out" object data
by rev_1318 (Chaplain) on May 20, 2005 at 08:06 UTC
    How about:
    my $self = shift; %$self = ();

    Paul

      This is the one you should use.

      This is also the scheme by which ImageMagick's Perl bindings module suggests you "remove all images without removing the object." Well, it's an array reference, so they document the idiom as @$magick = (), but same thing.

      --
      [ e d @ h a l l e y . c c ]

Re: "cleaning out" object data
by aukjan (Friar) on May 20, 2005 at 08:57 UTC
    I would use your own solution, but put this in an '_init', so I could set some initial values (if needed). This could then be called from your 'new' or when you want to 'reset' the object:
    sub new { my $class = shift; my $self = {}; bless($self, $class); $self->_init; return $self; } sub _init { my $self = shift; foreach my $key (keys(%{$self})) { delete $self->{$key}; } $self->{hours_on_the_job} = 0; $self->{people_shot} = 0; etc... }
    Then you can just call $self->_init; whenever you wish to reset the data.

    .:| If it can't be fixed .. Don't break it |:.

      I like your thinking, but I'd suggest not calling it init. 'reset' or something similar might be a better idea.

      It's common practice, when you're dealing with inheritance, to make sure you call all of the init-type functions in the inherited object. (the easiest way is to just call $self->NEXT::_init(), if you're using NEXT

      Of course, if you were to use an _init() function as you've described, you'll end up trashing whatever other people have set up, as well.

      Ideally, you would just clear out the keys that you were making use of, so you don't end up treading on someone else's toes... but I'd still move the destructive part of the code out to a seperate function that is not called by the _init() or new() routines.

        Thanks for pointing out $self->NEXT::... to me... I have not been doing too much OO, but I have encountered problems for which the NEXT module is a good option! I will be busy making some code changes now :)

        .:| If it can't be fixed .. Don't break it |:.
Re: "cleaning out" object data
by BrowserUk (Patriarch) on May 20, 2005 at 11:32 UTC

    You could do

    delete @$self{ keys %$self };

    but that is really just syntatic sugar for what you had and it's hard to see what else you are hoping for?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: "cleaning out" object data
by merlyn (Sage) on May 20, 2005 at 12:12 UTC
    Are you sure you're ready for "object oriented programming"? In all the years I've done OOP (25 years now), I've never wanted to "clean out all the data without deleting the object itself". Maybe if you state what your real problem is, we can reply with a more in-band way of accomplishing this.

    I know that if I saw code like your delete, I'd pull one of those Scooby-Do scrunchy-face "HunH?" expressions. So, even if this accomplishes what you want in the short-term, you're destroying your long term maintainability. Perl code already gets a bad slap too often for that—no point in increasing that by going down the path you've started to choose.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Very well, the specific application for which I need this is an object that stores information regarding an IRC connection. Channelnames, the nicks of the people on those channels, their modes, etc. etc. etc. However, should for some reason the connection fail all that info is moot and needs to disappear, and will be reassembled once the connection has been reestablished.

      Remember rule one...

        Ok, then you don't want to completely delete your instance vars. You want to reset the values of those vars to empty:
        sub reset_me { my $self = shift; $self->{known_nicks} = {}; $self->{known_channelnames} = {}; }
        In fact, you can use that code during your initialization as well. Then when you add a new instance var, you'll have a clean place to initialize it for everyone.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.


        update: And as an afterthought, this will also permit your class to be reused for subclasses more directly, since you won't be accidentally clearing out variables that your subclasses need to retain.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: "cleaning out" object data
by holli (Abbot) on May 20, 2005 at 07:48 UTC
    You could simply bless an empty hash with the classname of the object.
    $obj = bless {}, ref($obj);


    holli, /regexed monk/
      That would make $obj point to a new object within its own scope, but all other references to the original object would still be intact, as would the data stored in the original object. I'll make a small example in the OP to clarify.

      Remember rule one...

Re: "cleaning out" object data
by pelagic (Priest) on May 20, 2005 at 07:36 UTC
    How about:
    my $self = shift; $self = ();

    pelagic
      That was also my first thought, but that:
      - destroys the object if it isn't referenced elsewhere, or
      - does absolutely nothing if it is still referenced elsewhere.

      Basically, as it turns out, all that does is take the variable $self that previously contained a reference to a blessed object, and gives it a new value, a reference to a newly created anonymous hash. So unless $self was the only reference to the object(which it isn't in my specific case) absolutely nothing happens :-)

      edit: formatting

      Remember rule one...

        Yes, of course, you're absolutely right. So I'm afraid your somewhat unelegant solution is the best we can get.

        pelagic
      To obtain the requested effect you'd use rev_1318's solution below, i.e. assigning to %$self and not to $self. Typo?

      Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

      Don't fool yourself.
Re: "cleaning out" object data
by pelagic (Priest) on May 20, 2005 at 11:51 UTC
    Ok now, after frodo72's comment, I can't resist and give you a code example:
    use strict; use Data::Dumper; $Data::Dumper::Indent = 1; my $o1 = gugus->new; my $o2 = $o1; print 'This is before Forsaken_cleaning: ', Dumper $o1, $o2; $o1 = $o1->Forsaken_clean; print "\n", 'This is after Forsaken_cleaning: ', Dumper $o1, $o2; my $o3 = gugus->new; my $o4 = $o3; print "\n", 'This is before rev_1318_cleaning: ', Dumper $o3, $o4; $o3 = $o3->rev_1318_clean; print "\n", 'This is after rev_1318_cleaning: ', Dumper $o3, $o4; package gugus; sub new { my $class = shift; my $self = { 'k1' => 4711 }; bless $self, $class; return $self; } sub Forsaken_clean{ my $self = shift; foreach (keys %$self) { delete $self->{$_}; } $self; } sub rev_1318_clean{ my $self = shift; %$self = (); } __RESULT__ This is before Forsaken_cleaning: $VAR1 = bless( { 'k1' => 4711 }, 'gugus' ); $VAR2 = $VAR1; This is after Forsaken_cleaning: $VAR1 = bless( {}, 'gugus' ); $VAR2 = $VAR1; This is before rev_1318_cleaning: $VAR1 = bless( { 'k1' => 4711 }, 'gugus' ); $VAR2 = $VAR1; This is after rev_1318_cleaning: $VAR1 = 0; $VAR2 = bless( {}, 'gugus' );
    Unless someone comes up with a idea not mentioned here, OP's solution is the way to go (or if you like it sweetened BrowserUk's).

    pelagic
      Excuse me, but you're not being fair here. I really don't know why you need to re-assign to the $o1 and $o3 objects:
      # ... when you call Forsaken_clean $o1 = $o1->Forsaken_clean; # ... and when you call rev_1318_clean $o3 = $o3->rev_1318_clean;
      The assignment risks to spoil the left hand side without added value: the call itself already cleans up the objects. And it really does spoil $o3, as we can see comparing the cleaning functions:
      sub Forsaken_clean{ my $self = shift; foreach (keys %$self) { delete $self->{$_}; } $self; } sub rev_1318_clean{ my $self = shift; %$self = (); }
      The former has a $self as last statement, while the latter hasn't and it's returning nothing. Fixing is trivial, just add a line in rev_1318_clean:
      use strict; use Data::Dumper; $Data::Dumper::Indent = 1; my $o1 = gugus->new; my $o2 = $o1; print 'This is before Forsaken_cleaning: ', Dumper $o1, $o2; $o1 = $o1->Forsaken_clean; print "\n", 'This is after Forsaken_cleaning: ', Dumper $o1, $o2; my $o3 = gugus->new; my $o4 = $o3; print "\n", 'This is before rev_1318_cleaning: ', Dumper $o3, $o4; $o3 = $o3->rev_1318_clean; print "\n", 'This is after rev_1318_cleaning: ', Dumper $o3, $o4; package gugus; sub new { my $class = shift; my $self = { 'k1' => 4711 }; bless $self, $class; return $self; } sub Forsaken_clean{ my $self = shift; foreach (keys %$self) { delete $self->{$_}; } $self; } sub rev_1318_clean{ my $self = shift; %$self = (); $self; # This is the added line } __RESULT__ This is before Forsaken_cleaning: $VAR1 = bless( { 'k1' => 4711 }, 'gugus' ); $VAR2 = $VAR1; This is after Forsaken_cleaning: $VAR1 = bless( {}, 'gugus' ); $VAR2 = $VAR1; This is before rev_1318_cleaning: $VAR1 = bless( { 'k1' => 4711 }, 'gugus' ); $VAR2 = $VAR1; This is after rev_1318_cleaning: $VAR1 = bless( {}, 'gugus' ); $VAR2 = $VAR1;

      Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

      Don't fool yourself.
        I was not unfair I was just sloppy!
        The correct way is either with your line added or without the reassignements.

        I'm now wearing sackcloth and ashes!

        pelagic
Re: "cleaning out" object data
by brian_d_foy (Abbot) on May 21, 2005 at 02:07 UTC

    I often set up objects with an init() or reset() methods. I use the new() simply to create the object, and then let it call init() where everything gets populated, assigned, and so on. In that case, I would just call init() again to get the object back to its pristine state. You might have to include some code to make sure it only has the stuff you want (and nothing more).

    In your example, I would think you would want a new Guard object for each new guard on duty. You can still store the current Guard in the same variable in the main script, but once each Guard instance has its own object you can do things with the Guard coming off shift and the Guard going on shift. You can freeze the off-duty Guard object and thaw it out when it's time for him to work again. You could even keep an array of Guards around and rotate through them.

    Instead of exposing a Guard object to the main script though, what you really want is a Security object. Like you said, the Celebrity does not care about the Guard, only the Security. The Security object can handle shift changes and so on. As you rotate through the Guards, you can ask the Security object things like "Who's on duty?", but also, "Who was last on duty?" and "What did he remember"? You can't do that sort of thing if you wipe out the Guard's memory at every shift change.

    --
    brian d foy <brian@stonehenge.com>