Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > How to code an implicit $_ argument?

Reply
Thread Tools

How to code an implicit $_ argument?

 
 
Tim McDaniel
Guest
Posts: n/a
 
      08-19-2009
This is a function which trims leading and trailing whitespace from a
variable in-place, operating on $_ if there are no arguments provided.
(If the value is undef, it leaves it undef.) I feel a bit unclean
using "goto &sub" and hand-manipulation of @_. Opinions? Better ways
to do it?

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

#! /usr/bin/perl -w

use strict;

sub trim(;\$) {
if (@_ == 0) {
$_[0] = \$_;
goto &trim;
}
my $var = $_[0];
if (defined $$var) {
$$var =~ s/^\s+//;
$$var =~ s/\s+$//;
}
return $$var;
}

my $v = ' abc ';
trim $v;
if ($v ne 'abc') {
warn "\$v should be 'abc' but is '$v' instead.\n";
}

$_ = ' 123 ';
trim;
if ($_ ne '123') {
warn "\$_ should be '123' but is '$_' instead.\n";
}

exit 0;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

The only clean way I see to do it is this, but I don't know whether
later Perl interpreters notice and optimize the tail recursion.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

....
sub trim(;\$);
sub trim(;\$) {
if (@_ == 0) {
return trim($_);
}
my $var = $_[0];
if (defined $$var) {
$$var =~ s/^\s+//;
$$var =~ s/\s+$//;
}
return $$var;
}
....

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

In a recent message, someone mentioned that Perl 5.10 has the _
prototype character: if there is no matching argument, use $_.
But that doesn't work here, as the following code produces
Can't use string (" abc ") as a SCALAR ref while "strict refs"
in use at /home/tmcdaniel/local/test/084.pl line 7.
and "sub trim(\_)" causes
Malformed prototype for main::trim: \_ at
/home/tmcdaniel/local/test/084.pl line 15.

Also, where is _ documented? I don't see it in "man perlsub" for Perl
5.10 in Cygwin.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

....
sub trim(_) {
my $var = $_[0];
if (defined $$var) {
$$var =~ s/^\s+//;
$$var =~ s/\s+$//;
}
return $$var;
}
....

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

--
Tim McDaniel, http://www.velocityreviews.com/forums/(E-Mail Removed)
 
Reply With Quote
 
 
 
 
John W. Krahn
Guest
Posts: n/a
 
      08-19-2009
Tim McDaniel wrote:
> This is a function which trims leading and trailing whitespace from a
> variable in-place, operating on $_ if there are no arguments provided.
> (If the value is undef, it leaves it undef.) I feel a bit unclean
> using "goto &sub" and hand-manipulation of @_. Opinions? Better ways
> to do it?


sub trim {
if ( @_ ) {
s/^\s+//, s/\s+$// for @_;
}
else {
s/^\s+//, s/\s+$// for $_;
}
}



John
--
Those people who think they know everything are a great
annoyance to those of us who do. -- Isaac Asimov
 
Reply With Quote
 
 
 
 
Ilya Zakharevich
Guest
Posts: n/a
 
      08-19-2009
On 2009-08-19, Tim McDaniel <(E-Mail Removed)> wrote:
> sub trim(;\$) {
> if (@_ == 0) {
> $_[0] = \$_;
> goto &trim;
> }


push @_, $_ unless @_;

Etc,
Ilya
 
Reply With Quote
 
Tim McDaniel
Guest
Posts: n/a
 
      08-19-2009
In article <KJJim.77878$(E-Mail Removed)>,
John W. Krahn <(E-Mail Removed)> wrote:
>Tim McDaniel wrote:
>> This is a function which trims leading and trailing whitespace from
>> a variable in-place, operating on $_ if there are no arguments
>> provided. (If the value is undef, it leaves it undef.) I feel a
>> bit unclean using "goto &sub" and hand-manipulation of @_.
>> Opinions? Better ways to do it?

>
>sub trim {
> if ( @_ ) {
> s/^\s+//, s/\s+$// for @_;
> }
> else {
> s/^\s+//, s/\s+$// for $_;
> }
> }


Oo-er. So I should depend on

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).

instead of taking references explicitly or implicitly. Hrm. It's a
run-time error instead of compile-time, but it's much more elegant and
Perlish.

Two improvements occur to me.

> s/^\s+//, s/\s+$// for $_;


has the effect of creating a new $_ and aliasing $_ to, um, old $_,
which is not needed. It would be easier to just do

s/^\s+//, s/\s+$//;

> if ( @_ ) {
> s/^\s+//, s/\s+$// for @_;
> }
> else {
> s/^\s+//, s/\s+$// for $_;
> }


looks very symmetric and therefore elegant. But the individual
actions have to fit in a comma expression. Also, the set of
statements are repeated, so any change has to be done in two places --
in particular, one part of the spec is that undef should be silently
ignored, but the above code causes two "Use of uninitialized value $_
in substitution (s///)" warnings.

My first version defined a return value, but I don't really need it,
since the function changes variables. I prefer to always return an
explicit value, if nothing else to prevent accidental communication of
the function's last expression, causing the caller to accidentally
depend on the implementation.

I think I'll do something along these lines:

sub trim(@) {
foreach (@_ ? @_ : $_) {
if (defined $_) {
s/^\s+//;
s/\s+$//;
}
}
return undef;
}

or something equivalent. Maybe I will go for

defined($_) && (s/^\s+//, s/\s+$//) for @_ ? @_ : $_;

Many thanks for the insight.

--
Tim McDaniel, (E-Mail Removed)
 
Reply With Quote
 
Martijn Lievaart
Guest
Posts: n/a
 
      08-19-2009
On Wed, 19 Aug 2009 04:47:38 +0000, Tim McDaniel wrote:

> or something equivalent. Maybe I will go for
>
> defined($_) && (s/^\s+//, s/\s+$//) for @_ ? @_ : $_;
>


This will have unexpected results when $_ is defined, but you do give the
function arguments. Suddenly it works on $_ while you expected it to work
on @_.

Just drop the first test and it looks fine.

M4

 
Reply With Quote
 
Peter J. Holzer
Guest
Posts: n/a
 
      08-19-2009
On 2009-08-19 03:48, Ilya Zakharevich <(E-Mail Removed)> wrote:
> On 2009-08-19, Tim McDaniel <(E-Mail Removed)> wrote:
>> sub trim(;\$) {
>> if (@_ == 0) {
>> $_[0] = \$_;
>> goto &trim;
>> }

>
> push @_, $_ unless @_;


That pushes a copy of $_ onto @_.

hp
 
Reply With Quote
 
Willem
Guest
Posts: n/a
 
      08-19-2009
Tim McDaniel wrote:
) The only clean way I see to do it is this, but I don't know whether
) later Perl interpreters notice and optimize the tail recursion.
)
) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~
)
) ...
) sub trim(;\$);
) sub trim(;\$) {
) if (@_ == 0) {
) return trim($_);
) }
) my $var = $_[0];
) if (defined $$var) {
) $$var =~ s/^\s+//;
) $$var =~ s/\s+$//;
) }
) return $$var;
) }

I don't see why you would need prototyping for this:

sub trim
{
return trim($_) unless @_;
for (@_) {
s/^\s+//;
s/\s+$//;
}
return @_;
}

As far as I know, members of @_ are aliases for the function parameters, so
this should work directly on the given parameters.
And, of course, the for (@_) loop makes $_ aliases of the @_ members.

I wouldn't worry about the single tail-recursive call. If it turns out you
need the extra speed, just hand-inline the code.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
Reply With Quote
 
Tim McDaniel
Guest
Posts: n/a
 
      08-19-2009
In article <(E-Mail Removed)>,
Martijn Lievaart <(E-Mail Removed)> wrote:
>On Wed, 19 Aug 2009 04:47:38 +0000, Tim McDaniel wrote:
>
>> or something equivalent. Maybe I will go for
>>
>> defined($_) && (s/^\s+//, s/\s+$//) for @_ ? @_ : $_;
>>

>
>This will have unexpected results when $_ is defined, but you do give
>the function arguments. Suddenly it works on $_ while you expected it
>to work on @_.


Why do you say that? "man perlsyn" says that a

Any simple statement may optionally be followed by a SINGLE
modifier, just before the terminating semicolon (or block ending).
The possible modifiers are:

if EXPR
unless EXPR
while EXPR
until EXPR
foreach LIST

(Pity about that "SINGLE":
s/^\s+//, s/\s+$// if defined($_) for @_ ? @_ : $_;
would look cleaner.)

So "&&" doesn't bind more loosely than "for". The "defined($_)"
refers to the new $_ defined by "for", and s/// works on that $_.

I've tested the statement with
$_ = ' narf ';
my $v = ' abc ';
my $w = "\tzyx \r ";
trim($v, $w);
$v and $w were trimmed and $_ was untouched, as specified.

>Just drop the first test and it looks fine.


You didn't notice what I wrote in a previous article, that I want it
to silently ignore arguments that are undef, and not emit
"Use of uninitialized value $_ in substitution (s///)" messages.

It occured to me later that I could do

$_ and s/^\s+//, s/\s+$// for @_ ? @_ : $_;

But that's getting too much like line-noise for my taste.

--
Tim McDaniel, (E-Mail Removed)
 
Reply With Quote
 
Tim McDaniel
Guest
Posts: n/a
 
      08-19-2009
In article <(E-Mail Removed)>,
Ben Morrow <(E-Mail Removed)> wrote:
>
>Quoth (E-Mail Removed):
>> sub trim(_) {
>> my $var = $_[0];

>
> my $var = \$_[0];


Wait, you can *do* that?! I knew you could assign to $_[expr] itself
to change the caller's argument, but you can take a reference to it
and use that to change it too?

.... clickity click ...

Sumbitch, you can! And it works for the aliasing in foreach too
(presumably because it's the exact same concept).

-- Tim McDaniel, (E-Mail Removed)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~

#! /usr/bin/perl -w

use strict;

sub foo(_) {
my $var = \$_[0];
++$$var;
return undef;
}

$_ = 19;
my $v = 23;
foo($v);
print "v: $v\n";
($v == 24) || die;

foo();
print "_: $_\n";
($_ == 20) || die;

my @a = (123, 456, 789);
foreach (@a) {
my $var = \$_;
$$var *= 10;
}
foreach (@a) {
print "a value is $_\n";
die unless $_ % 10 == 0;
}

exit 0;
 
Reply With Quote
 
Ilya Zakharevich
Guest
Posts: n/a
 
      08-19-2009
On 2009-08-19, Peter J. Holzer <(E-Mail Removed)> wrote:
>>> if (@_ == 0) {
>>> $_[0] = \$_;
>>> goto &trim;
>>> }

>>
>> push @_, $_ unless @_;

>
> That pushes a copy of $_ onto @_.


Thanks, indeed. (I always wanted to implement

hard_set(\@array, $slot) = $var;

which would alias a slot in array/hash to a scalar...)

Thanks again,
Ilya

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
How to get warnings about implicit narrowing in c99 code jaime C Programming 4 06-06-2007 11:55 PM
Implicit localization Progman ASP .Net 2 03-02-2006 08:06 PM
Implicit rule PIX Nieuws Xs4all Cisco 3 11-30-2005 02:14 AM
Question on additional decimals in implicit conversion Jacob Java 7 10-03-2003 10:23 PM



Advertisments