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

regex transforms of object attributes

by Tuppence (Pilgrim)
on Jun 18, 2004 at 20:42 UTC ( [id://368057]=perlquestion: print w/replies, xml ) Need Help??

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

Hello all, I'm wondering if there is a more perlish way to do this:
my $content = $self->content; $content =~ s/foo/bar/g; $self->content($content)
content here is a simple get_set from Class::MethodMaker. Anyone have ideas for a cleaner way? The whole creation of the seperate $content variable for no other reason then to pass it to the regex bothers me. Thanks!

Replies are listed 'Best First'.
Re: regex transforms of object attributes
by BrowserUk (Patriarch) on Jun 18, 2004 at 21:11 UTC

    If your class lends itself to the technique, you could use an lvalue method and avoid the need to copy anything.

    #! perl -slw use strict; package Test; sub new{ my( $class, $value ) = @_; return bless { content => $value }, $class; } sub content : lvalue { my( $self ) = shift; $self->{ content }; } 1; package main; my $thing = Test->new( 'empty' ); print $thing->content; $thing->content = 'the quick brown fox'; print $thing->content; $thing->content =~ s[\b(.)][\U$1]g; print $thing->content; __END__ P:\test>368057 empty the quick brown fox The Quick Brown Fox

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon

      BrowserUk, I see a contradiction between your post.

      In Re^2: regex transforms of object attributes, you say (rightly) that encoding a hash key string in the caller code breaks the encapsulation of the object. Using an lvalue method is, however, almost just as wrong. An lvalue method in Perl cannot do a check on what value you set, or do some computation with the rhs expression (in case the implementation of the attribute changes), so it is IMO not much better than just using a hash key. This is the weakness of the OO support of perl.

        Mostly, I agree with you as I recognised in this thread from a while back--but a couple of self-quotes from my posts in this thread:

        If your class lends itself to the technique, ...

        And

        Of course, if the content has to be verified then using an lvalue sub will also break, but that a different argument entirely:)

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon

        They key difference is that an lvalue method uses a public interface and does not rely on implementation of the object. When choosing to have an lvalue method you choose to have a simple and limited interface, which you argue against, but you're not breaking encapsulation. So there are two different issues.

        ihb

Re: regex transforms of object attributes
by Joost (Canon) on Jun 18, 2004 at 20:55 UTC
Re:regex transforms of object attributes
by Roy Johnson (Monsignor) on Jun 18, 2004 at 21:28 UTC
    If modifying the content is something you often do, it would be kind of cool to have the set method take a block of code and act internally:
    $self->content(sub{s/foo/bar/g});
    where the content method would recognize the code ref and treat the current value as $_ within it. Something vaguely like:
    sub content { my $self = shift; if (@_) { my $set = shift; if (isa($set,"CODE")) { # fixed :-) &$set for $self->{'content'}; # Code to modify content (as + $_) gets executed } else { $self->{'content'} = $set; # Direct set } } $self->{'content'}; }
    Another spiffy (but experimental) technique would be to make the content method an lvalue subroutine. So you could write the one-liner
    $self->content =~ s/foo/bar/g; # instead of # $self->content(map { s/foo/bar/g } map $_, $self->content);

    We're not really tightening our belts, it just feels that way because we're getting fatter.

      You've got a bug:

      if (isa($set,"CODE")) { &set for $self->{'content'}; # Code to modify content }

      That tries to call the subroutine set(). What you mean is:

      if (isa($set,"CODE")) { $set->() for $self->{'content'} # execute the $set coderef }
Re: regex transforms of object attributes
by blokhead (Monsignor) on Jun 18, 2004 at 21:35 UTC
    lvalue methods are the way to do it all in one shot (see $foo->bar = 14;). Juerd has even written Attribute::Property so that you can do what you want with validation. You might consider using his module instead of MethodMaker. If you don't need the validation, then just making the method :lvalue is fine.

    Depending on the purpose and scope of your program, I'd probably avoid "cheating" and messing around inside the object as Plankton suggests ($obj->{content} =~ s///). It's usually considered bad OO form, as it breaks encapsulation. Of course, in Perl you're always free to break any rules you want!

    blokhead

Re: regex transforms of object attributes
by Plankton (Vicar) on Jun 18, 2004 at 21:07 UTC
    What's wrong with doing ...
    $self->{'content'} =~ s/foo/bar/g;
    ... you know just don't use the get_set method.
    update Added tested code.
    Plankton@Chum_Bucket:~/perl/perlmonks> cat test.pl #!/usr/local/bin/perl -w use strict; package Test; sub new{ my( $class, $value ) = @_; return bless { content => $value }, $class; } 1; package main; my $thing = Test->new( 'empty' ); print $thing->{'content'} . "\n"; $thing->{'content'} = 'the quick brown fox'; print $thing->{'content'} . "\n"; $thing->{'content'} =~ s[\b(.)][\U$1]g; print $thing->{'content'} . "\n"; Plankton@Chum_Bucket:~/perl/perlmonks> ./test.pl empty the quick brown fox The Quick Brown Fox

    Plankton: 1% Evil, 99% Hot Gas.

      Nice tested code:)

      The problem with doing it that way is that you build a dependancy upon the implementation of the class. You have to know that the class is based upon a hash and that the data return by the $obj->content(); method is stored in the hash under the key name 'content'.

      If the implementation of the class changes for some reason, then it will require modification to every calling app also. That's what is meant by 'breaking encapsulation' or 'tight coupling'.

      Eg. If the class called by your test code was implemented this way, it will break your test.

      package Test; my %contents; sub new{ my( $class, $value ) = @_; my $self = bless \rand, $class; $contents{ $self } = $value; return $self; } sub content : lvalue { my( $self ) = shift; $contents{ $self }; } 1;

      Of course, if the content has to be verified then using an lvalue sub will also break, but that a different argument entirely:)


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
        Well hopefully the implementation does not change. Yes I know famous last words. But lvalue is an experimental feature. That has its problems too.
        <update>It just occured to me that ...
        s/foo/bar/g;
        ... could take place in the class defintion. That wouldn't violate encapsulation would it? All one would have to do is add a method to the class. Something like ...
        sub s { my ( $obj, $pat, $sub ) = @_; $obj->{'content'} =~ s/$pat/$sub/g; }
        I wonder if it would be possible to use overload ... you know something like ...
        use overload "s" => \&s;

        Plankton: 1% Evil, 99% Hot Gas.
      Violating encapsulation simply to avoid a transient variable seems like a poor trade to me.
        Really? Why you say that? The only "encapsulation" here is by convention. Perl does not have syntax to enforce information hiding. Does dealing with the attribute cause the attribute to become uncapsulated? I don't think so. The use of a variable to store the attribute does and has more potential for misuse in the code. Some thing that "encapsulation" is suppose to prevent.

        Plankton: 1% Evil, 99% Hot Gas.
Re: regex transforms of object attributes
by shemp (Deacon) on Jun 18, 2004 at 20:58 UTC
    $self->content( $self->content =~ s/foo/bar/ );
    You may need to put the parenthesis () after the inner call to content (the accessor one) so that perl unambiguously knows what you're trying to do
Re: regex transforms of object attributes
by ambrus (Abbot) on Jun 18, 2004 at 21:03 UTC

    This is not specific to method calls. In perl, you sometimes need to create a variable to use s, y, chop or chomp.

Re: regex transforms of object attributes
by gaal (Parson) on Jun 21, 2004 at 13:56 UTC
    If this is a class implementation rather than client code, and if you can move over to Class::Accessor-land, you can use Class::Accessor::Ref for this kind of thing.
    ${ $self->_ref_content } =~ s/foo/bar/g;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://368057]
Approved by Paladin
Front-paged by Roy Johnson
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2024-04-23 15:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found