[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