How to code an implicit $_ argument?

Discussion in 'Perl Misc' started by Tim McDaniel, Aug 19, 2009.

  1. Tim McDaniel

    Tim McDaniel Guest

    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,
     
    Tim McDaniel, Aug 19, 2009
    #1
    1. Advertising

  2. 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
     
    John W. Krahn, Aug 19, 2009
    #2
    1. Advertising

  3. On 2009-08-19, Tim McDaniel <> wrote:
    > sub trim(;\$) {
    > if (@_ == 0) {
    > $_[0] = \$_;
    > goto &trim;
    > }


    push @_, $_ unless @_;

    Etc,
    Ilya
     
    Ilya Zakharevich, Aug 19, 2009
    #3
  4. Tim McDaniel

    Tim McDaniel Guest

    In article <KJJim.77878$>,
    John W. Krahn <> 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,
     
    Tim McDaniel, Aug 19, 2009
    #4
  5. 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
     
    Martijn Lievaart, Aug 19, 2009
    #5
  6. On 2009-08-19 03:48, Ilya Zakharevich <> wrote:
    > On 2009-08-19, Tim McDaniel <> wrote:
    >> sub trim(;\$) {
    >> if (@_ == 0) {
    >> $_[0] = \$_;
    >> goto &trim;
    >> }

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


    That pushes a copy of $_ onto @_.

    hp
     
    Peter J. Holzer, Aug 19, 2009
    #6
  7. Tim McDaniel

    Willem Guest

    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
     
    Willem, Aug 19, 2009
    #7
  8. Tim McDaniel

    Tim McDaniel Guest

    In article <>,
    Martijn Lievaart <> 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,
     
    Tim McDaniel, Aug 19, 2009
    #8
  9. Tim McDaniel

    Tim McDaniel Guest

    In article <>,
    Ben Morrow <> wrote:
    >
    >Quoth :
    >> 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,

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

    #! /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;
     
    Tim McDaniel, Aug 19, 2009
    #9
  10. On 2009-08-19, Peter J. Holzer <> 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
     
    Ilya Zakharevich, Aug 19, 2009
    #10
  11. On Wed, 19 Aug 2009 16:01:38 +0000, Tim McDaniel wrote:

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


    Oops, sorry. However, you may wonder if this terseness is worth this
    potential confusion.

    M4
     
    Martijn Lievaart, Aug 20, 2009
    #11
  12. Tim McDaniel

    Dr.Ruud Guest

    Ilya Zakharevich wrote:

    > (I always wanted to implement
    >
    > hard_set(\@array, $slot) = $var;
    >
    > which would alias a slot in array/hash to a scalar...)


    Data::Alias

    --
    Ruud
     
    Dr.Ruud, Aug 23, 2009
    #12
  13. On 2009-08-23, Christian Winter <> wrote:
    >> Thanks, indeed. (I always wanted to implement
    >>
    >> hard_set(\@array, $slot) = $var;
    >>
    >> which would alias a slot in array/hash to a scalar...)


    > IMHO that shouldn't be too difficult in xs


    Of course. The semantic of aliasing is much simpler to implement than
    that of copying the value.

    The situation is the same as with lvalue-subroutines: it is not that
    one needs tricks to make the return value writable - it is the
    opposite: Perl core makes extra steps to make the return value of
    "usual subroutines" non-modifiable.

    The harder part is to find IN WHICH MODULE one may put this
    functionality.

    Ilya

    P.S. And of course, passing the patch through (the mess of?) p5p
    might be still tricky. IIRC, I may sit on a hundred of patches
    to Perl implementing IMO very important functionality which
    were ignored by p5p...
     
    Ilya Zakharevich, Aug 23, 2009
    #13
  14. On 2009-08-23, Dr.Ruud <> wrote:
    > Ilya Zakharevich wrote:
    >
    >> (I always wanted to implement
    >>
    >> hard_set(\@array, $slot) = $var;
    >>
    >> which would alias a slot in array/hash to a scalar...)

    >
    > Data::Alias


    Can't locate Data/Alias.pm in @INC ...

    It should be in the core.

    Ilya
     
    Ilya Zakharevich, Aug 23, 2009
    #14
  15. Tim McDaniel

    Dr.Ruud Guest

    Ilya Zakharevich wrote:
    > Dr.Ruud:
    >> Ilya Zakharevich:


    >>> (I always wanted to implement
    >>>
    >>> hard_set(\@array, $slot) = $var;
    >>>
    >>> which would alias a slot in array/hash to a scalar...)

    >>
    >> Data::Alias

    >
    > Can't locate Data/Alias.pm in @INC ...
    >
    > It should be in the core.


    Chip is implementing ":=", see p5p.

    I am not sure that slot aliasing is included, but if so then it would
    simply look like:

    my @array = qw/ q w e r t y /;
    my $key := $array[ 2 ];

    or

    my $key = "e";
    my @array = qw/ q w # r t y /;
    $array[ 2 ] := $key;

    --
    Ruud
     
    Dr.Ruud, Aug 23, 2009
    #15
  16. Tim McDaniel

    Guest

    On Sun, 23 Aug 2009 22:29:08 +0200, "Dr.Ruud" <> wrote:

    >Ilya Zakharevich wrote:
    >> Dr.Ruud:
    >>> Ilya Zakharevich:

    >
    >>>> (I always wanted to implement
    >>>>
    >>>> hard_set(\@array, $slot) = $var;
    >>>>
    >>>> which would alias a slot in array/hash to a scalar...)
    >>>
    >>> Data::Alias

    >>
    >> Can't locate Data/Alias.pm in @INC ...
    >>
    >> It should be in the core.

    >
    >Chip is implementing ":=", see p5p.
    >
    >I am not sure that slot aliasing is included, but if so then it would
    >simply look like:
    >
    > my @array = qw/ q w e r t y /;
    > my $key := $array[ 2 ];

    ^^
    Suspiciously looks like a Pascal assignment operator.
    Lets get even more confusing.
    -sln
     
    , Aug 24, 2009
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bhushit Joshipura

    defaulting argument to previous argument

    Bhushit Joshipura, Dec 29, 2003, in forum: C++
    Replies:
    5
    Views:
    429
  2. Ben Kial
    Replies:
    1
    Views:
    727
    Eric Enright
    Nov 15, 2004
  3. S?ren Gammelmark
    Replies:
    1
    Views:
    1,989
    Eric Sosman
    Jan 7, 2005
  4. jaime
    Replies:
    4
    Views:
    362
    Keith Thompson
    Jun 7, 2007
  5. DeMarcus
    Replies:
    9
    Views:
    344
    DeMarcus
    Mar 4, 2010
Loading...

Share This Page