[tpm] Pub discussion (1)
Rob Janes
janes.rob at gmail.com
Fri Aug 29 01:55:08 PDT 2008
I've heard that this:
1. my ( $x, $y ) = @_;
is better than this:
2. my ( $x, $y ) = ( shift, shift );
which is the same as:
my $x = shift;
my $y = shift;
The advantages:
a. number 1 is faster (this is what I've heard. Anybody profiled this?)
b. number 1 leaves the aliased arguments still on the stack, available
to be modified if need be.
The disadvantages:
a. if you want to assign to the alias, you have to leave it on the
stack (you can't shift it off), and assign to it via
$_[nnn] = "xoxoxox";
b. if you want to assign to the alias, it has to be writable. I
cannot be an alias to "1234 tell me what you're looking for"
(a string constant). Usually you'll get an error when you try to
assign. Occasionally you'll get a core dump (unrecoverable
error in perl). I forget the circumstances. Something about
passing the alias to the read only to a core routine that
expects a writable.
c. the parameter must be supplied (undef isn't writable)
d. to be safe, declare your parameters like in sub x($$$@)
e. to be even more safe, check the SvREADONLY flag on
the variable. Use readonly() from Scalar::Util or Scalar::Readonly.
so, this is a possible usage:
use Scalar::Util qw(readonly);
...
sub do_logging($$$$) {
my ( $self, $in_message, $out_result_code, $inout_filehandle ) = @_;
## "$out_result_code" is just a place holder you can't use
## the variable for it's purpose
...
## let's say the logfile gets full, and we want to close and reopen
if ( !readonly( $_[3] ) ) {
if ( $rotate_logfile_now ) {
$inout_filehandle->close;
## compress it?
$self->gen_new_logfile_name;
$inout_filehandle = $_[3] = new IO::File "> $$self{LOGFILE}";
## of course, if you keep the LOGFILE name in $self, you could
## keep the filehandle there too, but that would blow the
## example
}
}
...
print $inout_filehandle "$level $timestamp $in_message\n"
if $inout_filehandle;
...
## set to $out_result_code position
if ( !readonly($_[2]) ) {
if ( $problems ) {
$_[2] = [ 0, 'OK', 'No Problems' ];
} else {
$_[2] = 0;
}
}
return $problems ? 0 : 1;
}
It only really works for scalars, although the scalars can be objects or
references. It could be a shared memory handle, or a message, or an
error trace stack. And there's no reason you couldn't put those in a
$self, why have them on the parameter list?
http://search.cpan.org/~gbarr/Scalar-List-Utils-1.19/lib/Scalar/Util.pm
http://search.cpan.org/~gozer/Scalar-Readonly-0.02/lib/Scalar/Readonly.pm
http://search.cpan.org/~roode/Readonly-1.03/Readonly.pm
http://search.cpan.org/~roode/Readonly-XS-1.04/XS.pm
Anyways, the filehandle example is kind of contrived. If I want to do
something like that I put the filehandle in $self, not on the parameter
list.
I think a better scenario is where you want the subroutines or methods
to return a simple boolean for success or fail, and put more complex
diagnostics into a spot on @_. Test if the spot is there and writeable
first. If not, don't bother giving up more revealing diagnostics.
There's other cases where you can change the "parameters". in foreach,
$_ will let you modify things in the foreach list. In map, $_ is an
alias to the current item in the list, and it can be modified, changing
the item in the list.
-rob
Abram Hindle wrote:
> I think you're totally correct. I think we were in the wrong here.
>
> as this:
>
> sub c { " lolcakes whatwhat " }
> c() =~ /([a-z]+)\s*([a-z]+)/g;
> print "t1 ".t1($1,$2),$/;
> c() =~ /([a-z]+)\s*([a-z]+)/g;
> print "t2 ".t2($1,$2),$/;
> c() =~ /([a-z]+)\s*([a-z]+)/g;
> print "t3 ".t3($1,$2),$/;
> c() =~ /([a-z]+)\s*([a-z]+)/g;
> print "t4 ".t4($1,$2),$/;
>
> sub t1 {
> ($a,$b) = @_;
> warn "$a $b";
> $b =~ /(a+)/;
> return $a;
> }
>
> sub t2 {
> $a = shift;
> $b = shift;
> warn "$a $b";
> $b =~ /(a+)/;
> return $a;
> }
>
> sub t3 {
> $a = $_[0];
> $b = $_[1];
> warn "$a $b";
> $b =~ /(a+)/;
> return $a;
> }
>
> sub t4 {
> warn "$_[0] $_[1]";
> $_[1] =~ /(a+)/;
> return $_[0];
> }
>
> produces this:
> lolcakes whatwhat at ref.pl line 14.
> t1 lolcakes
> lolcakes whatwhat at ref.pl line 22.
> t2 lolcakes
> lolcakes whatwhat at ref.pl line 30.
> t3 lolcakes
> lolcakes whatwhat at ref.pl line 36.
> t4 a
>
> We were warning against using @_ directly
> since it is aliased. If you look in the last example
> $_[0] has changed.
>
> And for review form perlsub:
>
> Any arguments passed in show up in the array @_. Therefore, if you
> called a function with two arguments, those would be stored in $_[0]
> and $_[1]. The array @_ is a local array, but its elements are aliases
> for the actual scalar parameters. In particular, if an element $_[0]
> is updated, the corresponding argument is updated (or an error occurs
> if it is not updatable). If an argument is an array or hash element
> which did not exist when the function was called, that element is cre‐
> ated only when (and if) it is modified or a reference to it is taken.
> (Some earlier versions of Perl created the element whether or not the
> element was assigned to.) Assigning to the whole array @_ removes that
> aliasing, and does not update any arguments.
>
> So we were wrong about case of using shift.
>
> abram
>
>
> Alex Beamish wrote:
>> Greetings,
>>
>> After Madison's presentation on Net::DBus tonight, we retreated to
>> Burgundy's where a number of interesting technical discussions popped
>> up. Among them were discussions as to whether
>>
>> my $self = shift;
>> my $value = shift;
>>
>> would mean changes to $self would be reflected the same way than if
>> $self and $value were collected in one fell swoop using
>>
>> my ( $self, $value ) = @_;
>>
>> My experiments consisted of three files, Obj1.pm:
>>
>> package Obj1;
>>
>> sub new
>> {
>> my $class = shift;
>> my $self = {};
>>
>> bless ( $self, $class );
>> return ( $self );
>> }
>>
>> sub add
>> {
>> my $self = shift;
>> my $value = shift;
>>
>> $self->{value} = $value;
>> }
>>
>> sub value
>> {
>> my $self = shift;
>> return ( $self->{value} );
>> }
>>
>> 1;
>>
>> And the almost identical Obj2.pm:
>>
>> package Obj2;
>>
>> sub new
>> {
>> my $class = shift;
>> my $self = {};
>>
>> bless ( $self, $class );
>> return ( $self );
>> }
>>
>> sub add
>> {
>> my ( $self, $value ) = @_;
>>
>> $self->{value} = $value;
>> }
>>
>> sub value
>> {
>> my $self = shift;
>> return ( $self->{value} );
>> }
>>
>> 1;
>>
>> And finally the test script:
>>
>> #!/usr/bin/perl -w
>> #
>> # Test creating Obj1 and Obj2 to see if methods that access arguments
>> # differently affect whether changes propogate back to the caller.
>> # Specifically, does
>> #
>> # my $self = shift;
>> # my ( $vars ) = @_;
>> #
>> # produce a different result than
>> #
>> # my ( $self, $vars ) = @_;
>>
>> use Obj1;
>> use Obj2;
>>
>> {
>> my $obj1 = Obj1->new();
>> $obj1->add("This is object one");
>> print "Obj1 value is " . $obj1->value() . "\n";
>>
>> my $obj2 = Obj2->new();
>> $obj2->add("This is object two");
>> print "Obj2 value is " . $obj2->value() . "\n";
>> }
>>
>> The resulting output of running test12.pl is
>>
>> [alex at foo tpm-August2008]$ perl -w test12.pl
>> Obj1 value is This is object one
>> Obj2 value is This is object two
>>
>> This suggests that both methods (two shifts, or a single pull from @_)
>> produce the same results.
>>
>> Have I misunderstood, or coded something incorrectly? I believe that
>> both approaches mean pass by reference, meaning that changes are
>> reflected.
>>
>
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> toronto-pm mailing list
> toronto-pm at pm.org
> http://mail.pm.org/mailman/listinfo/toronto-pm
More information about the toronto-pm
mailing list