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
| [reply] [d/l] |
|
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.
| [reply] |
|
| [reply] |
|
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
| [reply] [d/l] |
Re: regex transforms of object attributes
by Joost (Canon) on Jun 18, 2004 at 20:55 UTC
|
$self->content("bar".substr($self->content,3));
Assuming bar is at the beginning of the string.
Update: Otherwise (cheating)
s/foo/bar/, $self->content($_) for $self->content;
But you have to have the $_ variable there because s/// needs a variable to act upon. It's one the things that annoy me sometimes.
| [reply] [d/l] [select] |
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.
| [reply] [d/l] [select] |
|
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
}
| [reply] [d/l] [select] |
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!
| [reply] [d/l] |
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. |
| [reply] [d/l] [select] |
|
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
| [reply] [d/l] [select] |
|
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. |
| [reply] [d/l] [select] |
|
Violating encapsulation simply to avoid a transient variable seems like a poor trade to me.
| [reply] |
|
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. |
| [reply] |
|
|
|
|
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 | [reply] [d/l] |
|
sub content{
print $_[0];
return "foobar";
}
content( content() =~ s/foo/bar/ );
__END__
Can't modify non-lvalue subroutine call in substitution (s///) at test
+.pl line 6, near "s/foo/bar/ )"
| [reply] [d/l] |
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.
| [reply] [d/l] [select] |
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;
| [reply] [d/l] |